Heute werden wir darüber sprechen, was Iterator in Java ist und warum er benötigt wird.
Wie Sie wahrscheinlich bereits wissen, verfügt Java über eine wunderbare Collection-Schnittstelle, die die Iterator-Schnittstelle implementiert. Lassen Sie mich gleich einen Vorbehalt machen: Die Iterator-Schnittstelle sollte nicht mit dem Iterator-Muster in Java verwechselt werden! Schauen wir uns zur Verdeutlichung zunächst die Benutzeroberfläche an.
Stellen wir uns für eine Sekunde vor, dass es in Java keinen Iterator gibt. In diesem Fall muss jeder einzelne in die Tiefen der Sammlungen eintauchen und wirklich verstehen, was sich
Wörtlich kann „Iterator“ mit „brutale Gewalt “ übersetzt werden . Das heißt, es handelt sich um eine bestimmte Entität, die alle Elemente in der Sammlung durchlaufen kann. Darüber hinaus können Sie dies tun, ohne sich mit der internen Struktur und Anordnung von Sammlungen befassen zu müssen. |
ArrayList
von LinkedList
ihnen HashSet
unterscheidet TreeSet
.
Methoden, die der Iterator implementieren muss
boolean hasNext()
— Wenn im iterierbaren Objekt (derzeit eine Sammlung) noch Werte vorhanden sind, gibt die Methode zurück true
, wenn keine Werte mehr vorhanden sind false
. E next()
– gibt das nächste Element der Sammlung (Objekt) zurück. Wenn keine weiteren Elemente vorhanden sind (es gab keine check hasNext()
und wir haben aufgerufen, next()
als wir das Ende der Sammlung erreicht hatten), löst die Methode aus NoSuchElementException
. void remove()
- Entfernt das Element, das zuletzt von erhalten wurde next()
. Die Methode kann Folgendes auslösen:
UnsupportedOperationException
, wenn dieser Iterator die Methode nicht unterstütztremove()
(z. B. bei schreibgeschützten Sammlungen)IllegalStateException
, wenn die Methodenext()
noch nicht aufgerufen wurde oderremove()
bereits seit dem letzten Aufruf aufgerufen wurdenext()
.
for-each
. Seine Erweiterung ist ListIterator. Schauen wir uns weitere Java-Listeniteratormethoden an. Sie kennen sie höchstwahrscheinlich:
void add(E e)
– fügt ein ElementE
ein in ;List
boolean hasPrevious()
– wird zurückgegebentrue
, wennList
während der Rückwärtssuche Elemente vorhanden sind;int nextIndex()
– gibt den Index des nächsten Elements zurück;E previous()
– gibt das vorherige Blattelement zurück;int previousIndex()
– gibt den Index des vorherigen Elements zurück;void set(E e)
- ersetzt das beim letzten Aufruf zurückgegebene Elementnext()
oderprevious()
durch das Elemente
.
List
mit den Begrüßungszeilen für die Schüler:
List<String> list = new ArrayList<>();
list.add("Hallo");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
Jetzt besorgen wir uns einen Iterator dafür und geben alle enthaltenen Zeilen auf der Konsole aus:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Jetzt wird es einen „Engpass“ geben: Java Collections erweitern, wie Sie wahrscheinlich wissen (und wenn Sie es nicht wissen, finden Sie es heraus), die Schnittstelle Iterable
, aber das bedeutet nicht nur List
, dass Set
sie Queue
Iteratoren unterstützen. For java Map iterator
wird ebenfalls unterstützt, muss aber aufgerufen werden Map.entrySet()
:
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
next()
Dann gibt die Methode ein Objekt zurück Entry
, das ein „Schlüssel“-„Wert“-Paar enthält. Dann ist alles beim Alten mit List
:
while (mapIterator.hasNext()) {
Map.Entry<String, Integer> entry = mapIterator.next();
System.out.println("Key: " + entry.getKey());
System.out.println("Value: " + entry.getValue());
}
Du denkst: „Hör auf. Wir sprechen über die Schnittstelle und der Titel des Artikels lautet „Muster“. Das heißt, das Iteratormuster ist die Iteratorschnittstelle? Oder ist die Schnittstelle ein Muster? Wenn dieses Wort zum ersten Mal auftaucht, gebe ich Ihnen einen Hinweis: Ein Muster ist ein Entwurfsmuster, ein bestimmtes Verhalten, an das sich eine Klasse oder viele miteinander verbundene Klassen halten müssen. Ein Iterator in Java kann für jedes Objekt implementiert werden, dessen interne Struktur eine Iteration beinhaltet, und Sie können die Signatur der besprochenen Methoden ändern. Bei der Implementierung eines Musters kommt es vor allem auf die Logik an, die die Klasse einhalten muss. Die Iterator-SchnittstelleList, Set, Queue, Map
ist eine private Implementierung des gleichnamigen Musters, die nach Ermessen des Programmierers sowohl auf vorgefertigte Strukturen ( ) als auch auf andere angewendet wird . Durch die Erweiterung der Iterator-Schnittstelle implementieren Sie ein Muster, müssen die Schnittstelle jedoch nicht erweitern, um das Muster zu implementieren. Eine einfache Analogie: Alle Fische schwimmen, aber nicht alles, was schwimmt, ist ein Fisch. Als Beispiel habe ich beschlossen, ... das Wort zu nehmen. Genauer gesagt, ein Substantiv. Es besteht aus Teilen: Präfix, Wurzel, Suffix und Endung. Für Wortteile erstellen wir eine Schnittstelle WordPart
und Klassen, die diese erweitern: Prefix, Root, Suffix и Ending
:
interface WordPart {
String getWordPart();
}
static class Root implements WordPart {
private String part;
public Root(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
static class Prefix implements WordPart {
private String part;
public Prefix(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
static class Suffix implements WordPart {
private String part;
public Suffix(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
static class Ending implements WordPart {
private String part;
public Ending(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
Dann Word
enthält die Klasse (das Wort) Teile, und zusätzlich zu ihnen fügen wir eine Ganzzahl hinzu, die die Anzahl der Teile im Wort widerspiegelt:
public class Word {
private Root root;
private Prefix prefix;
private Suffix suffix;
private Ending ending;
private int partCount;
public Word(Root root, Prefix prefix, Suffix suffix, Ending ending) {
this.root = root;
this.prefix = prefix;
this.suffix = suffix;
this.ending = ending;
this.partCount = 4;
}
public Word(Root root, Prefix prefix, Suffix suffix) {
this.root = root;
this.prefix = prefix;
this.suffix = suffix;
this.partCount = 3;
}
public Word(Root root, Prefix prefix) {
this.root = root;
this.prefix = prefix;
this.partCount = 2;
}
public Word(Root root) {
this.root = root;
this.partCount = 1;
}
public Root getRoot() {
return root;
}
public Prefix getPrefix() {
return prefix;
}
public Suffix getSuffix() {
return suffix;
}
public Ending getEnding() {
return ending;
}
public int getPartCount() {
return partCount;
}
public boolean hasRoot() {
return this.root != null;
}
public boolean hasPrefix() {
return this.prefix != null;
}
public boolean hasSuffix() {
return this.suffix != null;
}
public boolean hasEnding() {
return this.ending != null;
}
Okay, wir haben vier überladene Konstruktoren (der Einfachheit halber nehmen wir an, wir können nur ein Suffix haben). Ein Substantiv kann nicht aus einem Präfix bestehen, daher legen wir für einen Konstruktor mit einem Parameter die Wurzel fest. Schreiben wir nun eine Implementierung des Iteratormusters: WordIterator, die zwei Methoden überschreibt: hasNext()
und next()
:
public class WordIterator implements Iterator<Word.WordPart> {
private Word word;
private int wordPartsCount;
public WordIterator(Word word) {
this.word = word;
this.wordPartsCount = word.getPartCount();
}
@Override
public boolean hasNext() {
if (wordPartsCount == 4) {
return word.hasPrefix() || word.hasRoot() || word.hasSuffix() || word.hasEnding();
} else if (wordPartsCount == 3) {
return word.hasPrefix() || word.hasRoot() || word.hasSuffix();
} else if (wordPartsCount == 2) {
return word.hasPrefix() || word.hasRoot();
} else if (wordPartsCount == 1) {
return word.hasRoot();
}
return false;
}
@Override
public Word.WordPart next() throws NoSuchElementException {
if (wordPartsCount <= 0) {
throw new NoSuchElementException("No more elements in this word!");
}
try {
if (wordPartsCount == 4) {
return word.getEnding();
}
if (wordPartsCount == 3) {
return word.getSuffix();
}
if (wordPartsCount == 2) {
return word.getPrefix();
}
return word.getRoot();
} finally {
wordPartsCount--;
}
}
}
Jetzt muss nur noch der Klasse der Iterator zugewiesen werden Word
:
public class Word implements Iterable<Word.WordPart> {
…
@Override
public Iterator<WordPart>iterator() {
return new WordIterator(this);
}
…
}
Lassen Sie uns nun eine morphemische Analyse des Wortes „Rush“ durchführen:
public class Main {
public static void main(String[] args) {
Word.Root root = new Word.Root("беж");
Word.Prefix prefix = new Word.Prefix("пере");
Word.Suffix suffix = new Word.Suffix("к");
Word.Ending ending = new Word.Ending("a");
Word word = new Word(root, prefix, suffix, ending);
Iterator wordIterator = word.iterator();
while (wordIterator.hasNext()) {
Word.WordPart part = (Word.WordPart) wordIterator.next();
System.out.println(part.getClass() + ": " + part.getWordPart());
}
}
}
Bitte beachten Sie, dass ich bei meiner Implementierung des Iteratormusters die folgende Ausgabereihenfolge gewählt habe:
- Ende
- Suffix
- Konsole
- Wurzel
GO TO FULL VERSION