0Pricing
Java Academy · Lektion

Einen eigenen Iterator implementieren

Erstellen Sie eine eigene Iterator-Klasse für eine einfache verkettete Liste oder eine Bereichsstruktur.

Einen eigenen Iterator implementieren ist eine kostenlose Java Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Java Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.

Benutzerdefinierter Iterator

Mit einem benutzerdefinierten Iterator können Sie vollständig steuern, wie eine Datenstruktur durchlaufen wird. In dieser Lektion wird Schritt für Schritt die Implementierung eines Iterators für eine verkettete Liste erläutert.

Die Node-Klasse

Definieren Sie zunächst die Knotenstruktur für eine einfach verkettete Liste.

class Node<T> {
    final T value;
    Node<T> next;

    Node(T value) {
        this.value = value;
    }
}

// Building a chain: 1 -> 2 -> 3
Node<Integer> head = new Node<>(1);
head.next = new Node<>(2);
head.next.next = new Node<>(3);

Implementierung des Iterators

Erstellen Sie eine innere Klasse, die Iterator<T> implementiert und einen Cursor auf den aktuellen Knoten besitzt.

import java.util.Iterator;
import java.util.NoSuchElementException;

class LinkedList<T> implements Iterable<T> {
    private Node<T> head;
    private int size;

    private class LinkedListIterator implements Iterator<T> {
        private Node<T> current = head; // cursor

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public T next() {
            if (!hasNext()) throw new NoSuchElementException();
            T value = current.value;
            current = current.next;
            return value;
        }
    }

    @Override
    public Iterator<T> iterator() {
        return new LinkedListIterator();
    }
}

addFirst und vollständige LinkedList

Fügen Sie die Möglichkeit hinzu, Knoten am Anfang einzufügen, und sehen Sie sich die vollständige funktionsfähige Klasse an.

class LinkedList<T> implements Iterable<T> {
    private Node<T> head;
    private int size;

    public void addFirst(T value) {
        Node<T> node = new Node<>(value);
        node.next = head;
        head = node;
        size++;
    }

    public void addLast(T value) {
        Node<T> node = new Node<>(value);
        if (head == null) { head = node; }
        else {
            Node<T> curr = head;
            while (curr.next != null) curr = curr.next;
            curr.next = node;
        }
        size++;
    }

    public int size() { return size; }

    @Override
    public Iterator<T> iterator() {
        return new LinkedListIterator();
    }
}

Den benutzerdefinierten Iterator verwenden

Nach der Implementierung des Iterable-Interfaces funktioniert die verkettete Liste in for-each-Schleifen und mit forEach.

LinkedList<String> list = new LinkedList<>();
list.addLast("Alice");
list.addLast("Bob");
list.addLast("Charlie");

// For-each loop works!
for (String name : list) {
    System.out.println(name);
}
// Alice
// Bob
// Charlie

// Stream also works (Java 8+)
list.forEach(name -> System.out.println("Hello, " + name));

Bereichsiterator

Ein einfacheres Beispiel: ein Iterator über einen numerischen Bereich ohne zugrunde liegende Datenstruktur.

class IntRange implements Iterable<Integer> {
    private final int start, end, step;

    IntRange(int start, int end, int step) {
        this.start = start; this.end = end; this.step = step;
    }
    IntRange(int start, int end) { this(start, end, 1); }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<>() {
            int current = start;
            public boolean hasNext() { return current < end; }
            public Integer next() {
                if (!hasNext()) throw new NoSuchElementException();
                int val = current;
                current += step;
                return val;
            }
        };
    }
}

for (int n : new IntRange(0, 10, 2)) System.out.print(n + " ");
// 0 2 4 6 8

Iterator für die Inorder-Traversierung eines Baums

Implementierung eines Inorder-Iterators für einen BST mit einem expliziten Stack – dies zeigt, wie Iteratoren eine rekursive Traversierung ersetzen können.

import java.util.*;

class BinaryTree<T extends Comparable<T>> {
    private record TreeNode<T>(T val, TreeNode<T> left, TreeNode<T> right) {}

    private TreeNode<T> root;

    public Iterator<T> inorderIterator() {
        Deque<TreeNode<T>> stack = new ArrayDeque<>();
        pushLeft(root, stack);
        return new Iterator<>() {
            public boolean hasNext() { return !stack.isEmpty(); }
            public T next() {
                TreeNode<T> node = stack.pop();
                pushLeft(node.right(), stack);
                return node.val();
            }
        };
    }

    private void pushLeft(TreeNode<T> node, Deque<TreeNode<T>> stack) {
        while (node != null) { stack.push(node); node = node.left(); }
    }
}

Fauler Iterator

Iteratoren können Werte verzögert erzeugen – erst beim Aufruf von next(). Das ist für unendliche Sequenzen nützlich.

class FibonacciIterator implements Iterator<Long> {
    private long a = 0, b = 1;

    @Override public boolean hasNext() { return true; } // infinite!

    @Override public Long next() {
        long result = a;
        long next = a + b;
        a = b;
        b = next;
        return result;
    }
}

Iterator<Long> fib = new FibonacciIterator();
for (int i = 0; i < 10; i++) System.out.print(fib.next() + " ");
// 0 1 1 2 3 5 8 13 21 34

Gefilterter Iterator

Ein dekorierender Iterator, der einen anderen Iterator umschließt und Elemente überspringt, die nicht zu einem Prädikat passen.

import java.util.*;
import java.util.function.*;

class FilterIterator<T> implements Iterator<T> {
    private final Iterator<T> source;
    private final Predicate<T> predicate;
    private T next;
    private boolean hasNext;

    FilterIterator(Iterator<T> source, Predicate<T> predicate) {
        this.source = source; this.predicate = predicate;
        advance();
    }

    private void advance() {
        hasNext = false;
        while (source.hasNext()) {
            T candidate = source.next();
            if (predicate.test(candidate)) { next = candidate; hasNext = true; break; }
        }
    }

    public boolean hasNext() { return hasNext; }
    public T next() { T val = next; advance(); return val; }
}

List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,10);
Iterator<Integer> evens = new FilterIterator<>(nums.iterator(), n -> n % 2 == 0);
while (evens.hasNext()) System.out.print(evens.next() + " ");
// 2 4 6 8 10

Integration von Iteratoren und Streams

Benutzerdefinierte Iteratoren können mit Spliterators.spliteratorUnknownSize() für Streams nutzbar gemacht werden.

import java.util.*;
import java.util.stream.*;

Iterator<Integer> rangeIt = new IntRange(1, 6).iterator();

Stream<Integer> stream = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(rangeIt, Spliterator.ORDERED),
    false // not parallel
);

int sum = stream.mapToInt(Integer::intValue).sum();
System.out.println(sum); // 15

Entfernen während der Iteration

Die optionale Methode remove() von Iterator entfernt das Element, das vom letzten Aufruf von next() zurückgegeben wurde – in benutzerdefinierten Iteratoren muss sie ausdrücklich implementiert werden.

class MutableLinkedList<T> implements Iterable<T> {
    // ... (full implementation)

    // Iterator with remove support
    private class RemovableIterator implements Iterator<T> {
        private Node<T> prev = null;
        private Node<T> current = head;

        public boolean hasNext() { return current != null; }
        public T next() {
            prev = (prev == null) ? null : current;
            T val = current.value;
            current = current.next;
            return val;
        }

        public void remove() {
            // Remove the last returned node
            if (prev == null) head = current;
            else prev.next = current;
            size--;
        }
    }
}

Checkliste für Iteratoren

Bei der Implementierung eines benutzerdefinierten Iterators:

  • Rufen Sie vor next() immer hasNext() auf
  • Lösen Sie bei einem leeren Iterator in next() NoSuchElementException aus, statt null zurückzugeben
  • Halten Sie den Iterator unabhängig vom Zustand der Collection (speichern Sie beispielsweise nicht die Größe der Collection zwischen)
  • Verwenden Sie bei Bedarf modCount, um gleichzeitige Änderungen zu erkennen

Kurztest

Welche Exception sollte next() auslösen, wenn keine weiteren Elemente vorhanden sind?

Zusammenfassung: Einen benutzerdefinierten Iterator implementieren

Die wichtigsten Erkenntnisse:

  • Implementieren Sie Iterator<T> mit hasNext(), next() und der optionalen Methode remove()
  • Verwalten Sie im Iterator ein Cursor-Feld, das auf das nächste Element zeigt
  • Lösen Sie in next() NoSuchElementException aus, wenn hasNext() false ergibt
  • Erstellen Sie für jeden Aufruf von iterator() eine neue Iteratorinstanz, damit die Cursor unabhängig sind
  • Faule Iteratoren erzeugen Werte bei Bedarf – das ist für unendliche Sequenzen nützlich
  • Umschließen Sie Iteratoren mit StreamSupport.stream(), um sie mit der Stream-API zu verbinden

Häufig gestellte Fragen

Ist die Lektion „Einen eigenen Iterator implementieren“ kostenlos?

Ja — der vollständige Text von „Einen eigenen Iterator implementieren“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Java Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Einen eigenen Iterator implementieren“?

Erstellen Sie eine eigene Iterator-Klasse für eine einfache verkettete Liste oder eine Bereichsstruktur. Du übst Java Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Java Academy zu starten?

Keine Vorkenntnisse erforderlich. Java Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.

Wie lange dauert die Lektion „Einen eigenen Iterator implementieren“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Java Academy-Lektion Code schreiben und ausführen?

Ja. Jede Java Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Die Verträge von Iterable und Iterator
  2. Einen eigenen Iterator implementieren
  3. ListIterator und bidirektionales Durchlaufen
  4. Fail-Fast- vs. Fail-Safe-Iteratoren
← Zurück zu Java Academy