EMZETT.
Login

Iterator

Kurz: Ein Objekt, das Element für Element durch eine Collection durchgeht, ohne deren interne Struktur kennen zu müssen — Grundlage der for-each-Schleife.

Genauer: Zentrale Methoden sind hasNext() (gibt es ein weiteres Element?) und next() (liefert es und rückt weiter). Anders als eine direkte for-Schleife über einen Index erlaubt ein Iterator über remove() das sichere Entfernen von Elementen WÄHREND der Iteration, ohne eine ConcurrentModificationException auszulösen.

Im Detail

List<String> namen = new ArrayList<>(List.of("Anna", "Ben", "Clara", "Ben"));
 
Iterator<String> it = namen.iterator();
while (it.hasNext()) {
    String name = it.next();
    if (name.equals("Ben")) {
        it.remove(); // sicheres Entfernen WÄHREND der Iteration
    }
}
System.out.println(namen); // [Anna, Clara]
 
// Das hier wirft eine ConcurrentModificationException:
for (String name : namen) {
    if (name.equals("Anna")) {
        namen.remove(name); // direktes Entfernen in einer for-each-Schleife - VERBOTEN
    }
}

Eine for-each-Schleife (for (String name : namen)) ist tatsächlich nur syntaktischer Zucker, der unter der Haube exakt einen Iterator mit hasNext()/next() erzeugt — deshalb funktioniert for-each überhaupt nur mit Typen, die das Iterable-Interface implementieren. Ruft man liste.remove(element) DIREKT innerhalb einer solchen Schleife auf (statt über den Iterator selbst), bemerkt der intern erzeugte Iterator die Strukturänderung und wirft beim nächsten next()-Aufruf eine ConcurrentModificationException, um zu verhindern, dass die Iteration in einen inkonsistenten Zustand gerät. Der explizite Weg über Iterator.remove() ist die einzige sichere Möglichkeit, während einer Iteration Elemente zu entfernen.

Eigenes Iterable implementieren

Jede eigene Klasse kann selbst iterierbar gemacht werden, indem sie Iterable<T> implementiert und eine iterator()-Methode bereitstellt, die einen passenden Iterator zurückgibt — das ist genau der Mechanismus, über den for-each auch mit selbstgeschriebenen Datenstrukturen funktioniert, nicht nur mit den Standard-Collections:

class Zahlenbereich implements Iterable<Integer> {
    private final int von, bis;
    Zahlenbereich(int von, int bis) { this.von = von; this.bis = bis; }
 
    public Iterator<Integer> iterator() {
        return new Iterator<>() {
            int aktuell = von;
            public boolean hasNext() { return aktuell <= bis; }
            public Integer next() { return aktuell++; }
        };
    }
}
 
for (int n : new Zahlenbereich(1, 5)) {
    System.out.println(n); // 1 2 3 4 5
}

ListIterator — bidirektionale Erweiterung

Für List-Implementierungen gibt es zusätzlich ListIterator, das den normalen Iterator um Rückwärts-Navigation (hasPrevious()/previous()), Ersetzen des aktuellen Elements (set()) und Einfügen an der aktuellen Position (add()) erweitert — nützlich, wenn während der Iteration nicht nur entfernt, sondern auch modifiziert oder eingefügt werden muss.

Siehe auch: For-Schleife, Collections, List