Iteratormuster
Teil des Abschnitts Objektorientierte Programmierung der Java-Journey von Coddy. Lektion 76 von 87.
Das Iterator Pattern ist ein Verhaltensmuster, das eine Möglichkeit bietet, nacheinander auf die Elemente einer Sammlung zuzugreifen, ohne deren zugrunde liegende Struktur offenzulegen. Ganz gleich, ob du mit einem Array, einer verketteten Liste oder einem Baum arbeitest: Der Iterator bietet dir eine einheitliche Möglichkeit, die Elemente zu durchlaufen.
Das Muster trennt die Durchlauflogik von der Sammlung selbst. Es umfasst zwei Hauptkomponenten: eine Iterator-Schnittstelle, die Methoden zum Durchlaufen der Elemente 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 Durchlaufzustand, sodass mehrere Iteratoren dieselbe Sammlung unabhängig voneinander durchlaufen können. 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());
}Die integrierten Schnittstellen Iterable und Iterator von Java folgen genau diesem Muster. Deshalb kannst du erweiterte for-Schleifen mit jeder Klasse verwenden, die Iterable implementiert. Das Iterator-Muster ist unverzichtbar, wenn du mehrere Möglichkeiten zur Durchquerung bereitstellen oder komplexe interne Strukturen vor dem Client-Code verbergen musst.
Aufgabe
EinfachErstellen wir ein Playlist-System mit dem Iterator Pattern! Du erstellst eine Musik-Playlist, die Songs speichert und einen benutzerdefinierten Iterator bereitstellt, mit dem sie der Reihe nach durchlaufen werden können, ohne offenzulegen, wie die Songs intern gespeichert sind. Das ist ein perfekter Anwendungsfall für das Iterator Pattern: Nutzer können deine Sammlung über eine klare, einheitliche Schnittstelle durchstöbern.
Du verteilst deinen Code auf vier Dateien:
Iterator.java: Definiere deine generischeIterator<T>-Schnittstelle mit zwei Methoden:hasNext(), die einen booleschen Wert zurückgibt, der angibt, ob weitere Elemente vorhanden sind, undnext(), die das nächste Element vom Typ T zurückgibt.Playlist.java: Erstelle die Aggregatklasse, die deine Songs enthält. DeinePlaylistsollte Songs in einem String-Array mit fester Kapazität speichern (die dem Konstruktor übergeben wird) und nachverfolgen, wie viele Songs hinzugefügt wurden. Füge eine MethodeaddSong(String song)hinzu, um Songs zur Playlist hinzuzufügen.Deine Playlist benötigt eine Methode
createIterator(), die einenIterator<String>zurückgibt. Implementiere sie, indem du eine private innere Klasse namensPlaylistIteratorerstellst, die deine Iterator-Schnittstelle implementiert. Dieser innere Iterator verwaltet seine eigene Indexposition und durchläuft das Song-Array, wobei er jeden Song der Reihe nach zurückgibt.Song.java: Erstelle eine einfache KlasseSong, die einen Songtitel kapselt. Sie sollte einen Konstruktor haben, der den Titel (String) entgegennimmt, eine MethodegetTitle()und eine MethodetoString(), diePlaying: [title]zurückgibt.Main.java: Füge dein Iterator-System zusammen! Du erhältst eine Eingabe: eine durch Kommas getrennte Liste von Songtiteln (zum Beispiel:Bohemian Rhapsody,Stairway to Heaven,Hotel California).Erstelle eine Playlist mit Platz für 10 Songs. Verarbeite die Eingabe und füge jeden Songtitel zur Playlist hinzu. Rufe dann einen Iterator von der Playlist ab und verwende ihn, um alle Songs zu durchlaufen und jeden in ein Song-Objekt einzuschließen und auszugeben (das als
Playing: [title]angezeigt wird).Nachdem du alle Songs durchlaufen hast, gib
Playlist complete!in einer neuen Zeile aus.
Du erhältst eine Eingabe: eine durch Kommas getrennte Zeichenfolge mit Songtiteln.
Bei der Eingabe Yesterday,Imagine,Let It Be wäre deine Ausgabe zum Beispiel:
Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!Beachte, wie deine Main-Klasse die Methoden hasNext() und next() des Iterators verwendet, um die Playlist zu durchlaufen, ohne etwas über die zugrunde liegende Array-Struktur wissen zu müssen. Der Iterator kapselt die gesamte Durchlauflogik und hält die interne Darstellung der Sammlung vor dem Client-Code verborgen!
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 Platz für 10 Songs
// TODO: Verarbeite die Eingabe (durch Kommas getrennt) und füge jeden Songtitel zur Playlist hinzu
// TODO: Hole mit createIterator() einen Iterator von der Playlist
// TODO: Verwende den Iterator, um alle Songs zu durchlaufen
// Erstelle für jeden Song ein Song-Objekt und gib es aus
// TODO: Gib nach dem Durchlaufen aller Songs "Playlist complete!" aus
}
}
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 Schlüsselwort thisMethodenFelder (Attribute)KonstruktormethodeÜberladen von KonstruktorenRückblick – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbung (extends)Das Schlüsselwort superÜberschreiben von Methoden (@Override)KonstruktorverkettungDie Object-KlasseEinfach- und mehrstufige VererbungWarum keine Mehrfachvererbung von KlassenRückblick – Mitarbeiterhierarchie7Spezielle Methoden & Object-Klasse
toString()-Methodeequals() und hashCode()clone()-MethodecompareTo() und ComparableComparator-SchnittstelleRückblick – Benutzerdefinierte Sortierung2Zugriffsmodifikatoren & Kapselung
Übersicht der ZugriffsebenenGetter- und Setter-MethodenInformationsverbergungDas Schlüsselwort finalRückblick – Bankkontoverwaltung5Polymorphismus
Grundlagen der MethodenüberladungMethodenüberschreibung (Laufzeit)Upcasting und DowncastingDer instanceof-OperatorAbstrakte Klassen und MethodenRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungAggregation vs. KompositionInnere, verschachtelte & anonyme KlassenEnums und Enum-MethodenRecords (Java 16+)Versiegelte Klassen (Java 17+)11Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterBuilder-MusterObserver-MusterStrategie-Muster14Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungsdienst3Klassen-Props & statische Member
Instanz- vs. statische VariablenStatische MethodenStatische BlöckeKonstanten (static final)Wiederholung – Counter & Utility6Schnittstellen & abstrakte Klassen
Einführung in InterfacesInterfaces implementierenMehrere Interfaces implementierenDefault & Static in InterfacesAbstrakte Klassen vs. InterfacesFunktionale InterfacesRückblick – Zahlungssystem9Generics
Einführung in GenericsGenerische KlassenGenerische MethodenGebundene TypparameterWildcards (?, extends, super)Rückblick – Generischer Container12Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterSchablonenmethodenmusterZustandsmusterKompositmusterIteratormusterÜbe selbstständig: Online-Java-Compiler