Java 8 Map Standardimplementierungsdetails

8

Ich habe die Standardimplementierung der neuen Java-8-Map-Methoden wie getOrDefault durchgesehen und etwas etwas seltsames bemerkt. Betrachten Sie zum Beispiel die Methode getOrDefault . Es ist wie folgt implementiert.

%Vor%

Nun, das "komische" Ding ist hier das Muster "Ergebnis der Zuweisung verwendet" in ((v = get(key)) != null . Nach meinem Wissen wird von diesem speziellen Muster abgeraten, da es die Lesbarkeit eher behindert. Eine IMO prägnantere Version wäre etwas nach dem Motto

%Vor%

Meine Frage ist, ob es einen bestimmten Grund gibt, das erstere über das letztere Muster hinaus zu verwenden, abgesehen von Codierungsstandards / Gewohnheiten. Insbesondere frage ich mich, ob diese beiden Versionen Spur und Leistung gleichwertig sind?

Das einzige, was ich mir vorstellen kann ist, dass der Compiler z. stellen Sie fest, dass containsKey in der Regel schneller auszuwerten ist und wertet es daher zuerst aus, aber soweit ich weiß, muss der Kurzschluss die Reihenfolge der Ausführung beibehalten (dies ist zumindest bei C der Fall).

EDIT: Nach @ruakh Vorschlag, hier sind die zwei Bytecodes (wie von javap -c generiert)

%Vor%

und

%Vor%

Ich muss zugeben, dass ich selbst nach Jahren Java-Codierung keine Ahnung habe, wie Java-Bytecode zu interpretieren ist. Könnte jemand freundlicherweise etwas Licht auf den Unterschied hier werfen?

    
incaseoftrouble 24.05.2017, 07:09
quelle

3 Antworten

8

Dies ist nur ein Stilproblem. Manche Leute bevorzugen den kompakteren Code, während andere einen längeren, aber einfacheren Code bevorzugen. Es scheint, einige der Entwickler Die Arbeit an der Java-Core-Bibliothek gehört zur ersten Gruppe.

Hinsichtlich der Effizienz sind beide Varianten identisch.

Schauen wir uns an, was der Compiler mit diesen beiden Varianten tatsächlich macht:

%Vor%

Lassen Sie uns nun den generierten Bytecode mit javap -c ExampleMap :

ausgeben %Vor%

Wie Sie sehen können, ist der Code größtenteils identisch. Der einzige kleine Unterschied ist in Linien 5 und 6 beider Methoden. Man dupliziert nur den obersten Wert des Stapels (Denken Sie daran, dass Java-Bytecode ein Stack-basiertes Maschinenmodell annimmt), während das andere lädt den (identischen) Wert aus einer Instanzvariablen.

Wenn der Just-in-Time-Compiler echten Maschinencode aus diesem Byte generiert Code wird es verschiedene Optimierungen durchführen, wie zum Beispiel, welche Werte zu wählen sind zurück in den RAM schreiben und in den CPU-Registern bleiben. Ich denke, es ist sicher zu Nehmen wir an, dass nach diesen Optimierungen kein Unterschied besteht links wie auch immer.

    
Rolf Schäuble 24.05.2017, 07:38
quelle
3

@ruakh hat im Kommentar darauf hingewiesen, dass containsKey method nicht aufgerufen wird, wenn v für Leistungszwecke nicht null ist.

%Vor%

Der Grund, warum @Eugene in seiner Antwort darauf hingewiesen hat.

    
holi-java 24.05.2017 07:26
quelle
2

Um ehrlich zu sein, hat mich das auch eine ganze Weile gestört. Und ich habe 3 andere gefunden, aber ähnliche Szenarien wie in den jdk-Quellen. Der erste ist derjenige, über den du gefragt hast, und hast eine Antwort bekommen, also wirst du nichts darüber sagen. Der zweite ist etwas anders:

%Vor%

Dies ist für extreme Bytecode-Optimierung erforderlich. Sie können dies testen, aber es erzeugt in diesem Fall einen kleineren Byte-Code. Und für eine Kern Bibliothek bedeutet kleiner viel besser.

Das dritte Beispiel ist dem vorherigen ähnlich, aber stell dir vor, dass lock volatile ist. Nun, da volatile Speichersemantiken hat, die sich von gewöhnlichen Variablen unterscheiden (es führt Speicherbarrieren ein), die Ihnen einen konsistenten Wert geben würden. Konsistenz in diesem Fall könnte mehr als nur diese Variable bedeuten, aber alle gespeicherten Werte vor der Speicher für diese bestimmte volatile (es ist, wie flüchtige Stoffe arbeiten ...).

    
Eugene 25.05.2017 07:47
quelle

Tags und Links