Was ist, wenn überhaupt, der Leistungsunterschied zwischen den folgenden beiden Schleifen?
for (Object o: objectArrayList) {
o.DoSomething();
}
und
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
java
performance
for-loop
eflles
quelle
quelle
Antworten:
Aus Punkt 46 in Effective Java von Joshua Bloch:
quelle
Alle diese Schleifen machen genau das Gleiche. Ich möchte sie nur zeigen, bevor ich meine zwei Cent einsetze.
Erstens die klassische Art, eine Liste zu durchlaufen:
Zweitens der bevorzugte Weg, da er weniger fehleranfällig ist (wie oft haben SIE die Sache "oops, gemischt die Variablen i und j in diesen Schleifen innerhalb von Schleifen" gemacht?).
Drittens die mikrooptimierte Schleife:
Jetzt die tatsächlichen zwei Cent: Zumindest als ich diese testete, war der dritte der schnellste, wenn Millisekunden gezählt wurden, wie lange es für jeden Schleifentyp gedauert hat, wobei eine einfache Operation einige Millionen Mal wiederholt wurde - dies war Java 5 mit jre1.6u10 unter Windows, falls jemand interessiert ist.
Während es zumindest so zu sein scheint, dass der dritte der schnellste ist, sollten Sie sich wirklich fragen, ob Sie das Risiko eingehen möchten, diese Gucklochoptimierung überall in Ihrem Schleifencode zu implementieren, da nach meinem Dafürhalten keine tatsächliche Schleife vorliegt. Normalerweise der zeitaufwändigste Teil eines echten Programms (oder ich arbeite nur auf dem falschen Gebiet, wer weiß). Und auch, wie ich im Vorwand für die Java- for-each-Schleife erwähnt habe (einige bezeichnen sie als Iterator-Schleife und andere als for-in-Schleife ), ist es weniger wahrscheinlich, dass Sie diesen einen dummen Fehler treffen, wenn Sie sie verwenden. Und bevor Sie darüber diskutieren, wie dies sogar schneller sein kann als die anderen, denken Sie daran, dass javac den Bytecode überhaupt nicht optimiert (na ja, fast überhaupt nicht), sondern nur kompiliert.
Wenn Sie sich jedoch für Mikrooptimierung interessieren und / oder Ihre Software viele rekursive Schleifen verwendet, sind Sie möglicherweise am dritten Schleifentyp interessiert. Denken Sie daran, Ihre Software sowohl vor als auch nach dem Ändern der for-Schleifen auf diese seltsame, mikrooptimierte zu überprüfen.
quelle
get(int)
, der andere verwendet einenIterator
. Überlegen Sie,LinkedList
wo die Leistung vonfor(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }
weitaus schlechter ist, da sieget(int)
n-mal ausgeführt wird.Die for-each-Schleife sollte im Allgemeinen bevorzugt werden. Der "get" -Ansatz ist möglicherweise langsamer, wenn die von Ihnen verwendete List-Implementierung keinen Direktzugriff unterstützt. Wenn beispielsweise eine LinkedList verwendet wird, entstehen Ihnen Durchquerungskosten, während für jeden Ansatz ein Iterator verwendet wird, der seine Position in der Liste verfolgt. Weitere Informationen zu den Nuancen der for-each-Schleife .
Ich denke, der Artikel ist jetzt hier: neuer Ort
Der hier gezeigte Link war tot.
quelle
Nun, die Auswirkungen auf die Leistung sind größtenteils unbedeutend, aber nicht Null. Wenn Sie sich JavaDoc der
RandomAccess
Schnittstelle ansehen :Und für jede Schleife wird eine Version mit Iterator verwendet, sodass
ArrayList
beispielsweise für jede Schleife nicht die schnellste ist.quelle
Iterable
s, jedoch keine Arrays. Für Arrays wird eine for-Schleife mit einer Indexvariablen verwendet. Informationen dazu finden Sie im JLS .Es scheint leider einen Unterschied zu geben.
Wenn Sie sich den generierten Bytecode für beide Arten von Schleifen ansehen, sind sie unterschiedlich.
Hier ist ein Beispiel aus dem Log4j-Quellcode.
In /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java haben wir eine statische innere Klasse namens Log4jMarker, die definiert:
Mit Standardschleife:
Mit für jeden:
Was ist mit DIESEM Oracle los?
Ich habe dies mit Java 7 und 8 unter Windows 7 versucht.
quelle
Es ist immer besser, den Iterator zu verwenden, als zu indizieren. Dies liegt daran, dass der Iterator höchstwahrscheinlich für die List-Implementierung optimiert ist, während indiziert (Aufruf von get) möglicherweise nicht. Zum Beispiel ist LinkedList eine Liste, aber die Indizierung durch ihre Elemente ist langsamer als die Iteration mit dem Iterator.
quelle
foreach macht die Absicht Ihres Codes klarer und dies wird normalerweise einer geringfügigen Geschwindigkeitsverbesserung vorgezogen - falls vorhanden.
Immer wenn ich eine indizierte Schleife sehe, muss ich sie etwas länger analysieren, um sicherzustellen, dass sie das tut, was ich denke. ZB Beginnt sie bei Null, schließt sie den Endpunkt ein oder aus usw.?
Die meiste Zeit verbringe ich damit, Code zu lesen (den ich geschrieben habe oder den jemand anderes geschrieben hat), und Klarheit ist fast immer wichtiger als Leistung. Es ist heutzutage einfach, die Leistung zu verwerfen, weil Hotspot so einen tollen Job macht.
quelle
Der folgende Code:
Gibt folgende Ausgabe auf meinem System:
Ich verwende Ubuntu 12.10 alpha mit OracleJDK 1.7 Update 6.
Im Allgemeinen optimiert HotSpot viele Indirektionen und einfache redundante Vorgänge. Im Allgemeinen sollten Sie sich also keine Sorgen machen, es sei denn, es gibt viele davon in Folge oder sie sind stark verschachtelt.
Auf der anderen Seite ist das indizierte Abrufen von LinkedList viel langsamer als das Aufrufen des nächsten Iterators für LinkedList, sodass Sie diesen Leistungseinbruch vermeiden und gleichzeitig die Lesbarkeit beibehalten können, wenn Sie Iteratoren verwenden (explizit oder implizit in jeder Schleife).
quelle
Selbst bei so etwas wie einer ArrayList oder einem Vektor, bei dem "get" eine einfache Array-Suche ist, hat die zweite Schleife noch zusätzlichen Overhead, den die erste nicht hat. Ich würde erwarten, dass es ein bisschen langsamer ist als das erste.
quelle
Die einzige Möglichkeit, dies sicher zu wissen, besteht darin, es zu bewerten, und selbst das ist nicht so einfach, wie es sich anhört . Der JIT-Compiler kann sehr unerwartete Dinge mit Ihrem Code tun.
quelle
Hier ist eine kurze Analyse des Unterschieds, den das Android-Entwicklungsteam herausgestellt hat:
https://www.youtube.com/watch?v=MZOf3pOAM6A
Das Ergebnis ist , dass es ist ein Unterschied, und in sehr zurückhaltenden Umgebungen mit sehr großen Listen könnte es ein spürbarer Unterschied. Bei ihren Tests dauerte die für jede Schleife doppelt so lange. Ihre Tests wurden jedoch über eine Arrayliste von 400.000 ganzen Zahlen durchgeführt. Die tatsächliche Differenz pro Element im Array betrug 6 Mikrosekunden . Ich habe nicht getestet und sie haben es nicht gesagt, aber ich würde erwarten, dass der Unterschied bei der Verwendung von Objekten anstelle von Grundelementen etwas größer ist, aber auch dann, wenn Sie keinen Bibliothekscode erstellen, bei dem Sie keine Ahnung haben, in welchem Umfang Sie gefragt werden Ich denke, der Unterschied ist es nicht wert, darüber nachzudenken.
quelle
Beim Variablennamen
objectArrayList
nehme ich an, dass dies eine Instanz von istjava.util.ArrayList
. In diesem Fall wäre der Leistungsunterschied nicht wahrnehmbar.Wenn es sich dagegen um eine Instanz von handelt
java.util.LinkedList
, ist der zweite Ansatz viel langsamer, daList#get(int)
es sich um eine O (n) -Operation handelt.Daher wird der erste Ansatz immer bevorzugt, es sei denn, der Index wird von der Logik in der Schleife benötigt.
quelle
Beide machen das Gleiche, aber für eine einfache und sichere Programmierung gibt es Möglichkeiten für fehleranfällige Anwendungen.
quelle
Es ist seltsam, dass niemand das Offensichtliche erwähnt hat - foreach weist Speicher zu (in Form eines Iterators), während eine normale for-Schleife keinen Speicher zuweist. Bei Spielen unter Android ist dies ein Problem, da der Garbage Collector regelmäßig ausgeführt wird. In einem Spiel soll der Müllsammler nicht laufen ... NIEMALS. Verwenden Sie also keine foreach-Schleifen in Ihrer Zeichen- (oder Render-) Methode.
quelle
Akzeptierte Antwort beantwortet die Frage, abgesehen vom Ausnahmefall von ArrayList ...
Da sich die meisten Entwickler auf ArrayList verlassen (zumindest glaube ich das)
Daher bin ich verpflichtet, hier die richtige Antwort hinzuzufügen.
Direkt aus der Entwicklerdokumentation: -
Die erweiterte for-Schleife (manchmal auch als "for-each" -Schleife bezeichnet) kann für Sammlungen verwendet werden, die die Iterable-Schnittstelle implementieren, sowie für Arrays. Bei Sammlungen wird ein Iterator zugewiesen, um Schnittstellenaufrufe an hasNext () und next () durchzuführen. Mit einer ArrayList ist eine handgeschriebene gezählte Schleife etwa dreimal schneller (mit oder ohne JIT), aber für andere Sammlungen entspricht die erweiterte for-Schleifensyntax genau der expliziten Verwendung von Iteratoren.
Es gibt verschiedene Alternativen zum Durchlaufen eines Arrays:
zero () ist am langsamsten, da die JIT die Kosten für das einmalige Abrufen der Array-Länge für jede Iteration durch die Schleife noch nicht optimieren kann.
one () ist schneller. Es zieht alles in lokale Variablen und vermeidet die Suche. Nur die Array-Länge bietet einen Leistungsvorteil.
two () ist für Geräte ohne JIT am schnellsten und für Geräte mit JIT nicht von one () zu unterscheiden. Es verwendet die erweiterte for-Schleifensyntax, die in Version 1.5 der Java-Programmiersprache eingeführt wurde.
Daher sollten Sie standardmäßig die erweiterte for-Schleife verwenden, jedoch eine handgeschriebene gezählte Schleife für die leistungskritische ArrayList-Iteration in Betracht ziehen.
quelle
Ja, die
for-each
Variante ist schneller als normalindex-based-for-loop
.for-each
Variante verwendetiterator
. Das Durchlaufen ist also schneller als eine normalefor
Schleife, die indexbasiert ist.Dies liegt daran
iterator
, dass sie für das Durchlaufen optimiert sind, da sie kurz vor dem nächsten Element und unmittelbar nach dem vorherigen Element zeigen . Einer der Gründe dafür,index-based-for-loop
langsam zu sein, ist, dass es jedes Mal , wenn es nicht mit dem ist, berechnen und sich zur Elementposition bewegen mussiterator
.quelle
quelle