it-swarm.com.de

Leistung von traditionell für Schleife gegen Iterator/Foreach in Java

Gibt es Leistungstestergebnisse, die beim Vergleich von herkömmlicher Schleife und Iterator beim Durchlaufen einer ArrayList, HashMap und anderer Sammlungen verfügbar sind?

Oder warum sollte ich Iterator für die Schleife oder umgekehrt verwenden?

58
Harish

Angenommen, das meintest du damit:

// traditional for loop
for (int i = 0; i < collection.size(); i++) {
  T obj = collection.get(i);
  // snip
}

// using iterator
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
  T obj = iter.next();
  // snip
}

// using iterator internally (confirm it yourself using javap -c)
for (T obj : collection) {
   // snip
}

Iterator ist schneller für Sammlungen ohne wahlfreien Zugriff (z. B. TreeSet, HashMap, LinkedList). Bei Arrays und ArrayLists sollten die Leistungsunterschiede vernachlässigbar sein. 

Edit: Ich glaube, dass Mikro-Benchmarking ziemlich bösartig ist, genau wie die frühzeitige Optimierung. Aber andererseits finde ich es gut, ein Gefühl für die Implikationen solcher recht unbedeutenden Dinge zu haben. Also habe ich einen kleinen Test ausgeführt

  • iterieren Sie über eine LinkedList und eine ArrayList
  • mit 100.000 "zufälligen" Zeichenketten
  • ihre Länge summieren (nur um zu vermeiden, dass der Compiler die gesamte Schleife wegoptimiert)
  • verwendung aller 3 Loop-Styles (Iterator für jeden mit Counter)

Die Ergebnisse sind für alle mit Ausnahme von "for with counter" mit LinkedList ähnlich. Alle anderen fünf benötigten weniger als 20 Millisekunden, um die gesamte Liste zu durchlaufen. Die Verwendung von list.get(i) auf einer LinkedList 100.000 Mal dauerte mehr als 2 Minuten (!) (60.000 Mal langsamer). Beeindruckend! :) Daher ist es am besten, einen Iterator zu verwenden (explizit oder implizit für jeden), insbesondere wenn Sie nicht wissen, mit welchem ​​Typ und mit welcher Größe Sie sich beschäftigen. 

79
sfussenegger

Der erste Grund für die Verwendung eines Iterators ist offensichtliche Korrektheit. Wenn Sie einen manuellen Index verwenden, kann es zu geringfügigen Fehlern kommen, die Sie nur sehen können, wenn Sie genau hinschauen: Haben Sie bei 1 oder bei 0 angefangen? Hast du mit length - 1 fertig? Haben Sie < oder <= verwendet? Wenn Sie einen Iterator verwenden, ist es viel einfacher zu erkennen, dass er wirklich das gesamte Array durchläuft. "Sag was du tust, tu was du sagst."

Der zweite Grund ist der einheitliche Zugriff auf unterschiedliche Datenstrukturen. Auf ein Array kann über einen Index effizient zugegriffen werden. Eine verknüpfte Liste wird jedoch am besten durch das Abrufen des letzten Elements durchsucht, auf das zugegriffen wird (andernfalls wird ein " Shlemiel the Painter " angezeigt). Eine Hashmap ist noch komplizierter. Durch die Bereitstellung einer einheitlichen Schnittstelle aus diesen und anderen Datenstrukturen (z. B. können Sie auch Baumdurchläufe durchführen), erhalten Sie wieder offensichtliche Korrektheit. Die Traversierungslogik muss nur einmal implementiert werden, und der Code, der sie verwendet, kann "genau sagen, was er tut und was er sagt".

22
Svante

Die Leistung ist in den meisten Fällen ähnlich.

Immer wenn ein Code eine Liste erhält und Schleifen darauf, gibt es einen bekannten Fall:
Der Iterator ist viel besser für alle List-Implementierungen, die RandomAccess nicht implementieren (Beispiel: LinkedList).

Der Grund ist, dass der Zugriff auf ein Element über diese Listen für diese Listen keine konstante Zeitoperation ist.

Daher können Sie den Iterator auch als robuster betrachten (bezüglich Implementierungsdetails).


Wie immer sollte die Leistung nicht aus Gründen der Lesbarkeit ausgeblendet werden.
Die Java5-foreach-Schleife ist ein großer Erfolg in diesem Aspekt :-)

4
KLE

Das glaube ich nicht

for (T obj : collection) {

berechnet jedes Mal .size () durch die Schleife und ist daher schneller als

for (int i = 0; i < collection.size(); i++) {
2
MeBigFatGuy

Ja, es macht einen Unterschied bei Sammlungen, die nicht wie etwa LinkedList auf wahlfreiem Zugriff basieren. Eine verknüpfte Liste wird intern durch Knoten implementiert, die auf den nächsten zeigen (beginnend mit einem Kopfknoten). 

Die get (i) -Methode in einer verknüpften Liste beginnt am Kopfknoten und navigiert durch die Links bis zum i'ten Knoten. Wenn Sie die verkettete Liste mit einer traditionellen for-Schleife durchlaufen, beginnen Sie jedes Mal erneut vom Kopfknoten, wodurch die Gesamtdurchquerung quadratisch wird.

for( int i = 0; i< list.size(); i++ ) {
    list.get(i); //this starts everytime from the head node instead of previous node
}

Die für jede Schleife durchläuft den Iterator, der aus der verknüpften Liste abgerufen wird, und ruft die next () -Methode auf. Der Iterator behält die Zustände des letzten Zugriffs bei und startet daher nicht immer den ganzen Weg vom Kopf.

for( Object item: list ) {
    //item element is obtained from the iterator's next method.
}
1
mickeymoon

Einer der besten Gründe, einen Iterator über die i ++ - Syntax zu verwenden, besteht darin, dass nicht alle Datenstrukturen den Direktzugriff unterstützen oder gar eine gute Leistung erbringen. Sie sollten auch auf die Listen- oder Erfassungsschnittstelle programmieren, damit Sie, wenn Sie später entschieden haben, dass eine andere Datenstruktur effizienter ist, diese ohne großen Eingriff austauschen können. In diesem Fall (bei der Codierung einer Schnittstelle) kennen Sie die Implementierungsdetails nicht unbedingt, und es ist wahrscheinlich klüger, dies auf die Datenstruktur selbst zu verschieben. 

1
Jason Tholstrup

Einer der Gründe, warum ich gelernt habe, bei jedem für sich zu bleiben, ist die Vereinfachung geschachtelter Schleifen, insbesondere von mehr als zwei dimensionalen Schleifen. Alle i, j und k, die Sie am Ende manipulieren, können sehr schnell verwirrend werden.

1
Ashton K

Verwenden Sie JAD oder JD-GUI gegen den generierten Code, und Sie werden feststellen, dass es keinen wirklichen Unterschied gibt. Der Vorteil des neuen Iterator-Formulars ist, dass es in Ihrer Codebase sauberer aussieht.

Bearbeiten: Ich sehe aus den anderen Antworten, dass Sie tatsächlich den Unterschied zwischen der Verwendung von get (i) und einem Iterator gemeint haben. Ich habe die ursprüngliche Frage genommen, um den Unterschied zwischen der alten und der neuen Verwendung des Iterators zu verstehen.

Die Verwendung von get (i) und die Verwaltung Ihres eigenen Zählers, insbesondere für die List-Klassen, ist aus den in der akzeptierten Antwort genannten Gründen keine gute Idee.

1
Paul Wagland

+1 zu dem, was sfussenegger gesagt hat. Zu Ihrer Information, unabhängig davon, ob Sie einen expliziten oder einen impliziten Iterator verwenden (d. H. Für jeden), hat keinen Einfluss auf die Leistung, da sie zum gleichen Bytecode kompiliert werden. 

0
erturne