Iterator vs für

75

Ich wurde in einem Interview gefragt, was der Vorteil der Verwendung des Iterators mit der for-Schleife ist oder was der Vorteil der Verwendung des for-Loops gegenüber dem Iterator ist.

Kann irgendjemand dies bitte beantworten, damit ich es in Zukunft beantworten kann, wenn ich vor ähnlichen Fragen stehe?

schaukeln
quelle
2
Eine for-each-Schleife verwendet einen Iterator unter der Abdeckung. Der Unterschied liegt nur in der Lesbarkeit (und damit in der Wartbarkeit).
Dawood ibn Kareem
1
Gut; warum hast du gesagt Iterator?
Boris die Spinne
2
Nichts zwingt Sie, sofort zu antworten. Sie können über die verschiedenen Arten von Sammlungen nachdenken, was jeder Code hinter den Kulissen tut, und Ihre Fähigkeit zum Denken zeigen, auch wenn Sie keine endgültige Antwort formulieren können. Das ist wichtig: denken können. Ich würde meine Antwort mit beginnen: "Nun, ich weiß es nicht. Ich denke, es hängt davon ab, was Sie tun möchten und welche Art von Sammlung es gibt. Es muss einen guten Grund geben, beide zu verwenden, aber es muss davon abhängen Stellen Sie sich vor, was mit einer ArrayList passiert. Dann mit einer LinkedList. usw. usw. "
JB Nizet
2
Ich stimme @JBNizet voll und ganz zu. Zu sagen, ich weiß es nicht viel besser als eine zufällige Option zu wählen - das gibt mir den Eindruck, dass Sie das beim Programmieren tun würden; Wählen Sie das erste aus, was Ihnen in den Sinn kommt, ohne nachzudenken. Sie könnten versuchen, Ihr Wissen in diesem Bereich zu erklären, als zu sagen, dass Sie nicht genug wissen, um eine fundierte Antwort zu geben.
Boris die Spinne
2
Beide aktuellen Antworten sind unvollständig. Zunächst gibt es zwei Arten von for-Schleifen, die sich sehr unterschiedlich verhalten. Einer verwendet Indizes (was nicht immer möglich ist) und der andere verwendet einen Iterator hinter den Kulissen. Sie haben also tatsächlich 3 Schleifen zum Vergleichen. Dann können Sie sie in verschiedenen Begriffen vergleichen: Leistung, Lesbarkeit, Fehleranfälligkeit, Fähigkeit. Ein Iterator kann Dinge tun, die eine foreach-Schleife nicht kann. Ein Iterator ist jedoch gefährlicher und weniger lesbar. Die Verwendung von Indizes für den Zugriff auf Elemente ist nur mit Listen möglich, jedoch nicht effizient, wenn es sich um eine verknüpfte Liste handelt. Es gibt also keinen "besten" Weg.
JB Nizet

Antworten:

126

Zunächst gibt es zwei Arten von for-Schleifen, die sich sehr unterschiedlich verhalten. Man verwendet Indizes:

for (int i = 0; i < list.size(); i++) {
    Thing t = list.get(i);
    ...
}

Diese Art von Schleife ist nicht immer möglich. Listen haben beispielsweise Indizes, Sets jedoch nicht, da es sich um ungeordnete Sammlungen handelt.

Die andere, die foreach-Schleife, verwendet einen Iterator hinter den Kulissen:

for (Thing thing : list) {
    ...
}

Dies funktioniert mit jeder Art von Iterable-Sammlung (oder Array)

Und schließlich können Sie einen Iterator verwenden, der auch mit jedem Iterable funktioniert:

for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    Thing t = it.next();
    ...
} 

Sie haben also tatsächlich 3 Schleifen zum Vergleichen.

Sie können sie in verschiedenen Begriffen vergleichen: Leistung, Lesbarkeit, Fehleranfälligkeit, Fähigkeit.

Ein Iterator kann Dinge tun, die eine foreach-Schleife nicht kann. Beispielsweise können Sie Elemente während der Iteration entfernen, wenn der Iterator dies unterstützt:

for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    Thing t = it.next();
    if (shouldBeDeleted(thing) {
        it.remove();
    }
} 

Listen bieten auch Iteratoren, die in beide Richtungen iterieren können. Eine foreach-Schleife iteriert nur vom Anfang bis zum Ende.

Ein Iterator ist jedoch gefährlicher und weniger lesbar. Wenn Sie nur eine foreach-Schleife benötigen, ist dies die am besten lesbare Lösung. Mit einem Iterator könnten Sie Folgendes tun, was ein Fehler wäre:

for (Iterator<Thing> it = list.iterator(); it.hasNext(); ) {
    System.out.println(it.next().getFoo());
    System.out.println(it.next().getBar());
} 

Eine foreach-Schleife lässt einen solchen Fehler nicht zu.

Die Verwendung von Indizes für den Zugriff auf Elemente ist bei Sammlungen, die von einem Array unterstützt werden, etwas effizienter. Wenn Sie jedoch Ihre Meinung ändern und eine LinkedList anstelle einer ArrayList verwenden, ist die Leistung plötzlich schrecklich, da list.get(i)die verknüpfte Liste bei jedem Zugriff alle ihre Elemente bis zur i-ten durchlaufen muss. Ein Iterator (und damit die foreach-Schleife) hat dieses Problem nicht. Es wird immer die bestmögliche Methode zum Durchlaufen von Elementen der angegebenen Sammlung verwendet, da die Sammlung selbst über eine eigene Iterator-Implementierung verfügt.

Meine allgemeine Faustregel lautet: Verwenden Sie die foreach-Schleife, es sei denn, Sie benötigen wirklich die Funktionen eines Iterators. Ich würde for-Schleife nur mit Indizes mit Arrays verwenden, wenn ich Zugriff auf den Index innerhalb der Schleife benötige.

JB Nizet
quelle
Wenn Sie einen Iterator verwenden, um ein oder mehrere Elemente in einer Sammlung zu entfernen, ist dies sicher. Wenn Sie eine for-each- oder for-Schleife verwenden, ist das Entfernen entweder nicht zulässig oder verursacht einen Fehler, da sich beim Entfernen während des Durchlaufens der Auflistung die Indizes der einzelnen Elemente in der Auflistung ändern. Verwenden Sie daher eine Reverse-for-Schleife oder einen Iterator, um die Einführung eines Fehlers zu verhindern. Siehe auch diese Antwort .
user504342
1
Das "ith", das Sie gesagt haben, ist eine Herausforderung in englischer Sprache.
MDP
Vielleicht kann er es durch "i-th" ersetzen?
Miroslav Todorov
17

Iterator Vorteil:

  • Möglichkeit, Elemente aus Sammlungen zu entfernen.
  • Fähigkeit, sich mit next()und vorwärts und rückwärts zu bewegen previous().
  • Möglichkeit zu überprüfen, ob mehr Elemente vorhanden sind oder nicht hasNext().

Die Schleife wurde nur für die Iteration über a Collectionentwickelt. Wenn Sie also nur über eine Iteration iterieren möchten Collection, ist es besser, eine Schleife wie zu verwenden for-Each, aber wenn Sie mehr wollen, als Sie Iterator verwenden können.

Salah
quelle
1
Und mit a können ListIteratorSie auch adddie Iteration an einem beliebigen Punkt starten.
Boris die Spinne
@Salah: Warum kann der Iterator Elemente entfernen, während die Sammlung iteriert wird?
Logbasex
12

Wenn Sie über eine Nummer (z. B. "i") auf Daten zugreifen, ist dies bei Verwendung des Arrays schnell. weil es direkt zum Element geht

Bei anderen Datenstrukturen (z. B. Baum, Liste) benötigt es jedoch mehr Zeit, da es vom ersten Element zum Zielelement beginnt. wenn Sie Liste verwenden. Es braucht Zeit O (n). es soll also langsam sein.

Wenn Sie den Iterator verwenden, weiß der Compiler, wo Sie sich befinden. es braucht also O (1) (weil es von der aktuellen Position ausgeht)

Schließlich, wenn Sie nur Array- oder Datenstrukturen verwenden, die direkten Zugriff unterstützen (z. B. Arraylist bei Java). "a [i]" ist gut. Wenn Sie jedoch eine andere Datenstruktur verwenden, ist der Iterator effizienter

user3392677
quelle
Ich sollte also sagen, dass es von der Anforderung abhängt.
Rocking
+1, um auf die Nachteile der Verwendung einer Indexschleife unter Java hinzuweisen Collections. Es ist erwähnenswert, dass eine verbesserte for-Schleife eine Iteratorunter der Haube verwendet.
Boris die Spinne
Ihre anderen Datenstrukturbeispiele sind nicht hilfreich - eine ArrayList bietet indizierten Zugriff. Vielleicht ein Update, um bestimmte Java-Sammlungen aufzunehmen? (zB ArrayList vs LinkedList)
Richard Miskin
10

Der Hauptunterschied zwischen Iterator und der klassischen for-Schleife besteht neben dem offensichtlichen Unterschied, dass ich Zugriff auf den Index des zu iterierenden Elements habe oder nicht, darin, dass die Verwendung von Iterator den Clientcode von der zugrunde liegenden Auflistungsimplementierung abstrahiert erarbeiten.

Wenn Ihr Code einen Iterator verwendet, entweder in dieser Form

for(Item element : myCollection) { ... }

diese Form

Iterator<Item> iterator = myCollection.iterator();
while(iterator.hasNext()) {    
    Item element = iterator.next();    
    ... 
}

oder diese Form

for(Iterator iterator = myCollection.iterator(); iterator.hasNext(); ) {
   Item element = iterator.next();
   ...
}

In Ihrem Code heißt es: "Die Art der Sammlung und ihre Implementierung sind mir egal, ich kümmere mich nur darum, dass ich ihre Elemente durchlaufen kann." Dies ist normalerweise der bessere Ansatz, da dadurch Ihr Code entkoppelt wird.

Auf der anderen Seite, wenn Sie die klassische for-Schleife wie in verwenden

for(int i = 0; i < myCollection.size(); i++) {
   Item element = myCollection.get(i);
   ...
}

Ihr Code sagt, ich muss die Art der Sammlung kennen, da ich ihre Elemente auf eine bestimmte Weise durchlaufen muss. Möglicherweise werde ich auch nach Nullen suchen oder ein Ergebnis basierend auf der Reihenfolge der Iteration berechnen. Dies macht Ihren Code anfälliger, da sich die Art der Sammlung, die Sie erhalten, zu irgendeinem Zeitpunkt auf die Funktionsweise Ihres Codes auswirkt.

Zusammenfassend lässt sich sagen, dass der Unterschied nicht so sehr in der Geschwindigkeit oder der Speichernutzung liegt, sondern vielmehr in der Entkopplung Ihres Codes, damit Sie flexibler mit Änderungen umgehen können.

xburgos
quelle
2

Im Gegensatz zu anderen Antworten möchte ich auf andere Dinge hinweisen;

Wenn Sie die Iteration an mehr als einer Stelle in Ihrem Code ausführen müssen, wird die Logik wahrscheinlich dupliziert. Dies ist eindeutig kein sehr erweiterbarer Ansatz. Stattdessen ist eine Möglichkeit erforderlich, die Logik für die Auswahl der Daten aus dem Code zu trennen, der sie tatsächlich verarbeitet.

Ein Iterator löst diese Probleme, indem er eine generische Schnittstelle zum Durchlaufen eines Datensatzes bereitstellt, sodass die zugrunde liegende Datenstruktur oder der zugrunde liegende Speichermechanismus - beispielsweise ein Array - ausgeblendet wird.

  • Iterator ist ein Konzept, keine Implementierung.
  • Ein Iterator bietet eine Reihe von Operationen zum Durchlaufen und Zugreifen auf Daten.
  • Ein Iterator kann jede Datenstruktur wie ein Array umschließen.
  • Einer der interessanteren und nützlicheren Vorteile der Verwendung von Iteratoren ist die Möglichkeit, einen anderen Iterator zu umschließen oder zu dekorieren, um die Rückgabewerte zu filtern
  • Ein Iterator kann threadsicher sein, während eine for-Schleife allein nicht sein kann, da sie direkt auf Elemente zugreift. Der einzige beliebte Thread-Sicherheits-Iterator ist, CopyOnWriteArrayListaber er ist bekannt und wird oft so erwähnenswert verwendet.

Dies ist aus dem Buch, dass es https://www.amazon.com/Beginning-Algorithms-Simon-Harris/dp/0764596748 ist

fgul
quelle
0

Ich bin über diese Frage gestolpert. Die Antwort liegt auf den Problemen, die Iterator zu lösen versucht:

  • Greifen Sie auf die Elemente eines Aggregatobjekts zu und durchlaufen Sie es, ohne dessen Darstellung verfügbar zu machen
  • Definieren Sie Durchlaufoperationen für ein Aggregatobjekt, ohne dessen Schnittstelle zu ändern
glücklich
quelle