Angenommen, ich habe eine ArrayList
ArrayList<MyClass> myList;
Und ich möchte bei Array anrufen, gibt es einen Leistungsgrund zu verwenden
MyClass[] arr = myList.toArray(new MyClass[myList.size()]);
Über
MyClass[] arr = myList.toArray(new MyClass[0]);
?
Ich bevorzuge den zweiten Stil, da er weniger ausführlich ist, und ich nahm an, dass der Compiler sicherstellen wird, dass das leere Array nicht wirklich erstellt wird, aber ich habe mich gefragt, ob das stimmt.
Natürlich macht es in 99% der Fälle keinen Unterschied auf die eine oder andere Weise, aber ich möchte einen konsistenten Stil zwischen meinem normalen Code und meinen optimierten inneren Schleifen beibehalten ...
java
performance
coding-style
itsadok
quelle
quelle
Antworten:
Gegenintuitiv ist die schnellste Version auf Hotspot 8:
Ich habe einen Mikro-Benchmark mit jmh durchgeführt. Die Ergebnisse und der Code sind unten aufgeführt. Dies zeigt, dass die Version mit einem leeren Array die Version mit einem vordefinierten Array durchweg übertrifft. Beachten Sie, dass das Ergebnis möglicherweise anders ausfällt, wenn Sie ein vorhandenes Array mit der richtigen Größe wiederverwenden können.
Benchmark-Ergebnisse (Punktzahl in Mikrosekunden, kleiner = besser):
Als Referenz der Code:
Ähnliche Ergebnisse, vollständige Analysen und Diskussionen finden Sie im Blog-Beitrag Arrays of Wisdom of the Ancients . Zusammenfassend lässt sich sagen, dass der JVM- und JIT-Compiler mehrere Optimierungen enthält, mit denen er kostengünstig ein neues Array mit der richtigen Größe erstellen und initialisieren kann. Diese Optimierungen können nicht verwendet werden, wenn Sie das Array selbst erstellen.
quelle
MyClass[] arr = myList.stream().toArray(MyClass[]::new);
die ich denke, dass sie langsamer sind. Außerdem würde ich gerne Benchmarks für den Unterschied zur Array-Deklaration sehen. Wie im Unterschied zwischen:MyClass[] arr = new MyClass[myList.size()]; arr = myList.toArray(arr);
undMyClass[] arr = myList.toArray(new MyClass[myList.size()]);
... oder sollte es keinen Unterschied geben? Ich denke, diese beiden sind ein Problem, das außerhalb dertoArray
Funktionsereignisse liegt. Aber hey! Ich hätte nicht gedacht, dass ich etwas über die anderen komplizierten Unterschiede lernen würde.toArray
direkte Aufruf (je kleiner die Größe,Ab ArrayList in Java 5 wird das Array bereits gefüllt, wenn es die richtige Größe hat (oder größer ist). Folglich
erstellt ein Array-Objekt, füllt es und gibt es an "arr" zurück. Andererseits
erstellt zwei Arrays. Das zweite ist ein Array von MyClass mit der Länge 0. Es wird also ein Objekt für ein Objekt erstellt, das sofort weggeworfen wird. Soweit der Quellcode dies vorschlägt, kann der Compiler / JIT diesen nicht so optimieren, dass er nicht erstellt wird. Darüber hinaus führt die Verwendung des Objekts mit der Länge Null zu Castings innerhalb der toArray () - Methode.
Siehe die Quelle von ArrayList.toArray ():
Verwenden Sie die erste Methode, damit nur ein Objekt erstellt wird, und vermeiden Sie (implizite, aber dennoch teure) Gussteile.
quelle
new Myclass[0]
ist schneller: shipilev.net/blog/2016/arrays-wisdom-ancientsVon JetBrains Intellij Idea Inspection:
quelle
Moderne JVMs optimieren in diesem Fall die Konstruktion reflektierender Arrays, sodass der Leistungsunterschied gering ist. Es ist keine gute Idee, die Sammlung zweimal in einem solchen Code zu benennen, daher würde ich die erste Methode vermeiden. Ein weiterer Vorteil der zweiten ist, dass sie mit synchronisierten und gleichzeitigen Sammlungen funktioniert. Wenn Sie eine Optimierung vornehmen möchten, verwenden Sie das leere Array erneut (leere Arrays sind unveränderlich und können gemeinsam genutzt werden) oder verwenden Sie einen Profiler (!).
quelle
private static final MyClass[] EMPTY_MY_CLASS_ARRAY = new MyClass[0]
verhindert nicht, dass das zurückgegebene Array durch Reflektion erstellt wird, verhindert jedoch, dass jedes Mal ein zusätzliches Array erstellt wird.MyClass[] arr = myList.stream().toArray(MyClass[]::new);
Würde es bei synchronisierten und gleichzeitigen Sammlungen helfen oder schaden? Und warum? Bitte.toArray überprüft, ob das übergebene Array die richtige Größe hat (dh groß genug, um zu den Elementen aus Ihrer Liste zu passen), und verwendet dies in diesem Fall. Wenn die Größe des Arrays kleiner als erforderlich ist, wird folglich reflexartig ein neues Array erstellt.
In Ihrem Fall ist ein Array der Größe Null unveränderlich und kann daher sicher zu einer statischen Endvariablen erhoben werden, wodurch Ihr Code möglicherweise ein wenig sauberer wird und das Array nicht bei jedem Aufruf erstellt wird. Innerhalb der Methode wird ohnehin ein neues Array erstellt, sodass die Lesbarkeit optimiert wird.
Die schnellere Version besteht wahrscheinlich darin, das Array mit der richtigen Größe zu übergeben. Wenn Sie jedoch nicht nachweisen können, dass dieser Code ein Leistungsengpass darstellt, ziehen Sie die Lesbarkeit der Laufzeitleistung vor, bis das Gegenteil bewiesen ist.
quelle
Der erste Fall ist effizienter.
Das liegt daran, dass im zweiten Fall:
Die Laufzeit erstellt tatsächlich ein leeres Array (mit der Größe Null) und erstellt dann innerhalb der toArray-Methode ein weiteres Array, das den tatsächlichen Daten entspricht. Diese Erstellung erfolgt mithilfe von Reflection mit dem folgenden Code (entnommen aus jdk1.5.0_10):
Durch die Verwendung des ersten Formulars vermeiden Sie die Erstellung eines zweiten Arrays und den Reflektionscode.
quelle
Der zweite ist geringfügig besser lesbar, aber es gibt so wenig Verbesserungen, dass es sich nicht lohnt. Die erste Methode ist schneller, ohne Nachteile zur Laufzeit, also verwende ich sie. Aber ich schreibe es auf die zweite Art, weil es schneller zu tippen ist. Dann kennzeichnet meine IDE es als Warnung und bietet an, es zu beheben. Mit einem einzigen Tastendruck wird der Code vom zweiten in den ersten Typ konvertiert.
quelle
Die Verwendung von 'toArray' mit dem Array der richtigen Größe ist besser, da die Alternative zuerst das Array mit der Größe Null und dann das Array mit der richtigen Größe erstellt. Wie Sie sagen, ist der Unterschied jedoch wahrscheinlich vernachlässigbar.
Beachten Sie außerdem, dass der Javac-Compiler keine Optimierung durchführt. Heutzutage werden alle Optimierungen zur Laufzeit von den JIT / HotSpot-Compilern durchgeführt. Mir sind keine Optimierungen in Bezug auf 'toArray' in JVMs bekannt.
Die Antwort auf Ihre Frage ist also weitgehend eine Frage des Stils, sollte jedoch aus Gründen der Konsistenz Teil aller Codierungsstandards sein, die Sie einhalten (ob dokumentiert oder anderweitig).
quelle
Beispielcode für Ganzzahl:
quelle