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.
Aufgabe
EinfachLassen 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 generischesIterator<T>-Interface mit zwei Methoden:hasNext(), die einen booleschen Wert zurückgibt, der angibt, ob weitere Elemente existieren, undnext(), die das nächste Element vom Typ T zurückgibt.Playlist.java: Erstellen Sie die Aggregat-Klasse, die Ihre Songs enthält. IhrePlaylistsollte 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 eineaddSong(String song)-Methode hinzu, um Songs zur Playlist hinzuzufügen.Ihre Playlist benötigt eine
createIterator()-Methode, die einenIterator<String>zurückgibt. Implementieren Sie dies, indem Sie eine private innere Klasse namensPlaylistIteratorerstellen, 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 einfacheSong-Klasse, die einen Songtitel kapselt. Sie sollte einen Konstruktor haben, der den Titel (String) entgegennimmt, einegetTitle()-Methode und einetoString()-Methode, die"Playing: " + titlezurü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: " + titleangezeigt 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
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDas this-SchlüsselwortMethodenFelder (Attribute)Konstruktor-MethodeKonstruktor-ÜberladungRückblick – Einfacher Taschenrechner4Vererbung
Grundlagen der Vererbung (extends)Das super-SchlüsselwortMethoden überschreiben (@Override)Konstruktor-VerkettungDie Object-KlasseEinfache & mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-InterfaceZusammenfassung – Benutzerdefiniertes Sortieren2Zugriffsmodifikatoren & Kapselung
Überblick über ZugriffsebenenGetter- und Setter-MethodenInformation HidingDas final-SchlüsselwortRückblick – Bankkonto-Manager5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenZusammenfassung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, geschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Sealed Classes (Java 17+)11Design Patterns Teil 1
Einführung in Design PatternsSingleton-PatternFactory-PatternBuilder-PatternObserver-PatternStrategy-Pattern14Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung3Klasseneigenschaften & Statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Zusammenfassung – Zähler & Utility6Interfaces & Abstrakte Klassen
Einführung in InterfacesInterfaces implementierenImplementierung mehrerer InterfacesDefault- & statische Methoden in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesZusammenfassung - Zahlungssystem9Generics
Einführung in GenericsGenerische KlassenGenerische MethodenEingeschränkte TypparameterWildcards (?, extends, super)Zusammenfassung – Generische Container12Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterState-MusterComposite-MusterIterator-Muster