Menu
Coddy logo textTech

Iterator-Muster

Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy — Lektion 76 von 87.

Das Iterator-Muster (Iterator Pattern) ist ein Verhaltensmuster, das eine Möglichkeit bietet, sequenziell auf Elemente einer Sammlung zuzugreifen, ohne deren zugrunde liegende Struktur offenzulegen. Egal, ob Sie mit einem Array, einer verketteten Liste oder einem Baum arbeiten, der Iterator bietet Ihnen eine einheitliche Möglichkeit, die Elemente zu durchlaufen.

Das Muster trennt die Traversierungslogik von der Sammlung selbst. Es umfasst zwei Hauptkomponenten: ein Iterator-Interface, das Methoden zum Durchlaufen von Elementen definiert, und ein Iterable (oder Aggregat), das Iteratoren für seine Sammlung erstellt:

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

Der Iterator verwaltet seinen eigenen Traversierungszustand, was es mehreren Iteratoren ermöglicht, dieselbe Sammlung unabhängig voneinander zu durchlaufen. Clients verwenden den Iterator, ohne zu wissen, wie die Sammlung ihre Daten speichert:

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Javas integrierte Iterable- und Iterator-Schnittstellen folgen genau diesem Muster, weshalb Sie erweiterte for-Schleifen mit jeder Klasse verwenden können, die Iterable implementiert. Das Iterator-Muster ist essenziell, wenn Sie mehrere Traversierungsmethoden bereitstellen oder komplexe interne Strukturen vor dem Client-Code verbergen müssen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Playlist-System mit dem Iterator-Pattern erstellen! Sie werden eine Musik-Playlist erstellen, die Songs speichert und einen benutzerdefinierten Iterator bereitstellt, um diese nacheinander zu durchlaufen – ohne offenzulegen, wie die Songs intern gespeichert sind. Dies ist ein perfekter Anwendungsfall für das Iterator-Pattern, da es Benutzern ermöglicht, durch Ihre Sammlung über eine saubere, einheitliche Schnittstelle zu navigieren.

Sie werden Ihren Code auf vier Dateien verteilen:

  • Iterator.java: Definieren Sie Ihr generisches Iterator<T>-Interface mit zwei Methoden: hasNext(), die einen booleschen Wert zurückgibt, der angibt, ob weitere Elemente existieren, und next(), die das nächste Element vom Typ T zurückgibt.
  • Playlist.java: Erstellen Sie die Aggregat-Klasse, die Ihre Songs enthält. Ihre Playlist sollte Songs in einem String-Array mit einer festen Kapazität (die an den Konstruktor übergeben wird) speichern und verfolgen, wie viele Songs hinzugefügt wurden. Fügen Sie eine addSong(String song)-Methode hinzu, um Songs zur Playlist hinzuzufügen.

    Ihre Playlist benötigt eine createIterator()-Methode, die einen Iterator<String> zurückgibt. Implementieren Sie dies, indem Sie eine private innere Klasse namens PlaylistIterator erstellen, die Ihr Iterator-Interface implementiert. Dieser innere Iterator verwaltet seine eigene Indexposition und durchläuft das Songs-Array, wobei er jeden Song der Reihe nach zurückgibt.

  • Song.java: Erstellen Sie eine einfache Song-Klasse, die einen Songtitel kapselt. Sie sollte einen Konstruktor haben, der den Titel (String) entgegennimmt, eine getTitle()-Methode und eine toString()-Methode, die "Playing: " + title zurückgibt.
  • Main.java: Führen Sie Ihr Iterator-System zusammen! Sie erhalten eine Eingabe: eine durch Kommas getrennte Liste von Songtiteln (zum Beispiel: "Bohemian Rhapsody,Stairway to Heaven,Hotel California").

    Erstellen Sie eine Playlist mit einer Kapazität für 10 Songs. Analysieren Sie die Eingabe und fügen Sie jeden Songtitel zur Playlist hinzu. Fordern Sie dann einen Iterator von der Playlist an und verwenden Sie ihn, um alle Songs zu durchlaufen, wobei Sie jeden Song in ein Song-Objekt eingepackt ausgeben (was als "Playing: " + title angezeigt wird).

    Nachdem Sie alle Songs durchlaufen haben, geben Sie "Playlist complete!" in einer neuen Zeile aus.

Sie erhalten eine Eingabe: einen durch Kommas getrennten String mit Songtiteln.

Zum Beispiel wäre bei der Eingabe "Yesterday,Imagine,Let It Be" Ihre Ausgabe:

Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!

Beachten Sie, wie Ihre Main-Klasse die Methoden hasNext() und next() des Iterators verwendet, um die Playlist zu durchlaufen, ohne etwas über die zugrunde liegende Array-Struktur zu wissen. Der Iterator kapselt die gesamte Traversierungslogik und hält die interne Darstellung der Sammlung vor dem Client-Code verborgen!

Spickzettel

Das Iterator-Muster (Iterator Pattern) ist ein Verhaltensmuster, das eine Möglichkeit bietet, sequenziell auf Elemente einer Sammlung zuzugreifen, ohne deren zugrunde liegende Struktur offenzulegen.

Das Muster trennt die Traversierungslogik von der Sammlung selbst unter Verwendung von zwei Hauptkomponenten:

  • Iterator-Schnittstelle: definiert Methoden zum Durchlaufen von Elementen
  • Iterable/Container: erstellt Iteratoren für seine Sammlung

Grundstruktur

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

Implementierungsbeispiel

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

Verwendung

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Der Iterator verwaltet seinen eigenen Traversierungszustand, was es mehreren Iteratoren ermöglicht, dieselbe Sammlung unabhängig voneinander zu durchlaufen. Javas integrierte Iterable- und Iterator-Schnittstellen folgen diesem Muster und ermöglichen erweiterte for-Schleifen.

Probier es selbst

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Erstelle eine Playlist mit einer Kapazität für 10 Songs
        
        // TODO: Verarbeite die Eingabe (kommagetrennt) und füge jeden Songtitel zur Playlist hinzu
        
        // TODO: Erhalte einen Iterator von der Playlist mit createIterator()
        
        // TODO: Verwende den Iterator, um alle Songs zu durchlaufen
        // Für jeden Song: Erstelle ein Song-Objekt daraus und gib es aus
        
        // TODO: Gib "Playlist complete!" aus, nachdem alle Songs durchlaufen wurden
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung