Da ich etwas neu in der Java-Sprache bin, versuche ich mich mit allen (oder zumindest nicht pathologischen) Möglichkeiten vertraut zu machen, wie man eine Liste (oder vielleicht andere Sammlungen) durchläuft, sowie mit den Vor- oder Nachteilen der einzelnen.
Bei einem bestimmten List<E> list
Objekt kenne ich die folgenden Möglichkeiten, um alle Elemente zu durchlaufen:
Grundlegend für Schleife (natürlich gibt es auch Äquivalente while
/ do while
Schleifen)
// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
E element = list.get(i);
// 1 - can call methods of element
// 2 - can use 'i' to make index-based calls to methods of list
// ...
}
Hinweis: Wie @amarseillan hervorhob, ist dieses Formular eine schlechte Wahl für die Iteration über List
s, da die tatsächliche Implementierung der get
Methode möglicherweise nicht so effizient ist wie bei Verwendung von a Iterator
. Beispielsweise müssen LinkedList
Implementierungen alle Elemente vor i durchlaufen, um das i-te Element zu erhalten.
Im obigen Beispiel gibt es keine Möglichkeit für die List
Implementierung, "ihren Platz zu speichern", um zukünftige Iterationen effizienter zu gestalten. Für a spielt ArrayList
es keine Rolle, denn die Komplexität / Kosten von get
sind konstante Zeit (O (1)), während sie für a LinkedList
proportional zur Größe der Liste (O (n)) sind.
Weitere Informationen zur rechnerischen Komplexität der integrierten Collections
Implementierungen finden Sie in dieser Frage .
Verbesserte for-Schleife ( in dieser Frage gut erklärt )
for (E element : list) {
// 1 - can call methods of element
// ...
}
Iterator
for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// ...
}
ListIterator
for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// 3 - can use iter.add(...) to insert a new element into the list
// between element and iter->next()
// 4 - can use iter.set(...) to replace the current element
// ...
}
Funktionelles Java
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach , Stream.forEach , ...
(Eine Kartenmethode aus der Stream-API von Java 8 (siehe Antwort von @ i_am_zero).)
In Java 8-Auflistungsklassen, die Iterable
(z. B. alle List
s) implementieren , gibt es jetzt eine forEach
Methode, die anstelle der oben gezeigten for-Schleifenanweisung verwendet werden kann . (Hier ist eine andere Frage , die einen guten Vergleich bietet.)
Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
// (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
// being performed with each item.
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).
Welche anderen Möglichkeiten gibt es, wenn überhaupt?
(Übrigens, mein Interesse beruht überhaupt nicht auf dem Wunsch, die Leistung zu optimieren . Ich möchte nur wissen, welche Formulare mir als Entwickler zur Verfügung stehen.)
List
Schnittstelle spezifisch?Antworten:
Die drei Formen der Schleife sind nahezu identisch. Die erweiterte
for
Schleife:die gemäß ist, Java Language Specification , identisch in der Tat auf die explizite Verwendung eines Iterators mit einem traditionellen
for
Schleife. Im dritten Fall können Sie den Listeninhalt nur ändern, indem Sie das aktuelle Element entfernen, und zwar nur dann, wenn Sie dies über dieremove
Methode des Iterators selbst tun . Mit der indexbasierten Iteration können Sie die Liste auf beliebige Weise ändern. Wenn Sie jedoch Elemente hinzufügen oder entfernen, die vor dem aktuellen Index stehen, besteht die Gefahr, dass Ihre Schleife Elemente überspringt oder dasselbe Element mehrmals verarbeitet. Sie müssen den Schleifenindex richtig anpassen, wenn Sie solche Änderungen vornehmen.In allen Fällen
element
wird auf das eigentliche Listenelement verwiesen. Keine der Iterationsmethoden erstellt eine Kopie von irgendetwas in der Liste. Änderungen am internen Status vonelement
werden immer im internen Status des entsprechenden Elements in der Liste angezeigt.Grundsätzlich gibt es nur zwei Möglichkeiten, eine Liste zu durchlaufen: mithilfe eines Index oder mithilfe eines Iterators. Die erweiterte for-Schleife ist nur eine syntaktische Verknüpfung, die in Java 5 eingeführt wurde, um die mühsame explizite Definition eines Iterators zu vermeiden. Für beide Arten können Sie mit im Wesentlichen banale Varianten kommen mit
for
,while
oderdo while
Blöcke, aber sie alle laufen auf die gleiche Sache (oder, besser gesagt, zwei Dinge).BEARBEITEN: Wie @ iX3 in einem Kommentar hervorhebt, können Sie mit a
ListIterator
das aktuelle Element einer Liste festlegen, während Sie iterieren. Sie müsstenList#listIterator()
stattdessen verwendenList#iterator()
, um die Schleifenvariable zu initialisieren (die natürlich als aListIterator
und nicht als deklariert werden müssteIterator
).quelle
e = iterator.next()
,e = somethingElse
ändert sich nur, worauf sich das Objekte
bezieht, anstatt den tatsächlichen Speicher zu ändern, aus demiterator.next()
der Wert abgerufen wurde.e
wird nicht geändert, was in der Liste enthalten ist. du musst anrufenlist.set(index, thing)
. Sie können den Inhalt vone
(z. B.e.setSomething(newValue)
) ändern. Um jedoch zu ändern, welches Element in der Liste gespeichert ist, während Sie es iterieren, müssen Sie sich an eine indexbasierte Iteration halten.e
müsste ich eine dere
Methoden aufrufen , um Änderungen am Inhalt von vorzunehmen, da die Zuweisung nur den Zeiger ändert (entschuldigen Sie mein C).Integer
undString
es wäre nicht möglich, den Inhalt mitfor-each
oderIterator
Methoden zu ändern - müsste das Listenobjekt selbst manipulieren, um es durch Elemente zu ersetzen. Ist das richtig?Beispiel für jede in der Frage aufgeführte Art:
ListIterationExample.java
quelle
Die Basisschleife wird nicht empfohlen, da Sie die Implementierung der Liste nicht kennen.
Wenn das eine LinkedList war, jeder Aufruf von
würde über die Liste iterieren, was zu N ^ 2 Zeitkomplexität führen würde.
quelle
Eine Iteration im JDK8-Stil:
quelle
In Java 8 gibt es mehrere Möglichkeiten, Sammlungsklassen zu durchlaufen.
Verwenden von Iterable forEach
Die implementierten Sammlungen
Iterable
(z. B. alle Listen) verfügen jetzt über eineforEach
Methode. Wir können die in Java 8 eingeführte Methodenreferenz verwenden .Verwenden von Streams forEach und forEachOrdered
Wir können eine Liste auch mit Stream wie folgt durchlaufen :
Wir sollen lieber
forEachOrdered
über ,forEach
weil das VerhaltenforEach
ausdrücklich nicht deterministisch ist , wo alsforEachOrdered
eine Aktion für jedes Element dieses Stroms, in der Begegnung Reihenfolge des Stroms , wenn der Strom eine definierte Begegnung Ordnung hat. ForEach garantiert daher nicht, dass die Bestellung beibehalten wird.Der Vorteil von Streams ist, dass wir bei Bedarf auch parallele Streams verwenden können. Wenn das Ziel nur darin besteht, die Artikel unabhängig von der Bestellung zu drucken, können wir den parallelen Stream wie folgt verwenden:
quelle
Ich weiß nicht, was Sie für pathologisch halten, aber lassen Sie mich einige Alternativen nennen, die Sie vorher nicht gesehen haben könnten:
Oder seine rekursive Version:
Auch eine rekursive Version des Klassikers
for(int i=0...
:Ich erwähne sie, weil Sie "etwas neu in Java" sind und dies interessant sein könnte.
quelle
while(!copyList.isEmpty()){ E e = copyList.remove(0); ... }
. Das ist effektiver als die erste Version;).Sie können forEach ab Java 8 verwenden:
quelle
Für eine Rückwärtssuche sollten Sie Folgendes verwenden:
Wenn Sie eine Position wissen möchten, verwenden Sie iterator.previousIndex (). Es ist auch hilfreich, eine innere Schleife zu schreiben, die zwei Positionen in der Liste vergleicht (Iteratoren sind nicht gleich).
quelle
Richtig, viele Alternativen sind aufgelistet. Am einfachsten und saubersten wäre es, die erweiterte
for
Anweisung wie folgt zu verwenden. DasExpression
ist von einem Typ, der iterierbar ist.Um beispielsweise die IDs von List <String> zu durchlaufen, können wir dies einfach tun:
quelle
In können
java 8
Sie dieList.forEach()
Methode mit verwendenlambda expression
, um eine Liste zu durchlaufen.quelle
eugene82
der Antwort undi_am_zero
der Antwort unterscheidet ?Sie können das erste und dritte Beispiel jederzeit mit einer while-Schleife und etwas mehr Code austauschen. Dies gibt Ihnen den Vorteil, dass Sie das Do-While verwenden können:
Natürlich kann dies zu einer NullPointerException führen, wenn list.size () 0 zurückgibt, da es immer mindestens einmal ausgeführt wird. Dies kann behoben werden, indem getestet wird, ob das Element null ist, bevor seine Attribute / Methoden verwendet werden. Trotzdem ist es viel einfacher und einfacher, die for-Schleife zu verwenden
quelle