Java Iterator-Implementierung - next () und hasNext () erzwingen Reihenfolge

8

Ich habe eine Implementierung von java.util.Iterator , die erfordert, dass der Aufruf von next() immer mit einem Aufruf von hasNext() fortgesetzt wird. (Dies liegt daran, dass die Ergebnisse asynchron in einer Umgebung mit mehreren Threads zurückgegeben werden und es nie klar ist, wie viele weitere Ergebnisse es gibt).

Wäre es "richtig", dies im JavaDoc richtig zu dokumentieren und dann ein RuntimeException zu werfen, wenn dies verletzt wurde. Oder dehnt sich die Iterator-Schnittstelle etwas zu weit aus?

Alle Gedanken geschätzt?

    
Dan 01.02.2010, 11:13
quelle

8 Antworten

18

Ich vermisse etwas hier, aber warum nicht hasNext() intern in Ihrer Implementierung aufrufen?

    
Fabian Steeg 01.02.2010, 11:17
quelle
15

Wenn% ce_de% aufgerufen wird, bevor hasNext() den next() Vertrag verletzt Sie sollten es wirklich neu schreiben, so dass iterator einfach ein next() wirft, wenn es kein zurückzugebendes Element gibt.

    
uckelman 01.02.2010 11:21
quelle
7

Ich kann mir vorstellen, dass Sie so etwas tun:

%Vor%

Das klingt OK für mich. Ich kann mir keine Situation vorstellen, in der Sie next ohne hasNext verwenden möchten - es wäre ein Rezept für Ausnahmen.

BEARBEITEN:

Das Dokument für hasNext() sagt :

  

Gibt true zurück, wenn die Iteration mehr Elemente enthält. (Mit anderen Worten, gibt true zurück, wenn next ein Element zurückgibt, anstatt eine Ausnahme auszulösen.)

Für mich verletzt die Implementierung nicht den Vertrag. Allerdings würde ich (wie Fabian Steeg impliziert) ( ) weiterhin next() als implementieren:

%Vor%

Ich meine, was kostet Sie das wirklich?

Sie müssen ein NoSuchElementException überprüfen und ausgeben, wie in der API-Vertrag . Entweder wird das Testen auf !hasNext() oder next == null diese Kriterien erfüllen, glaube ich, aber ich würde das erste bevorzugen.

Wenn jemand NoSuchElementException anstatt hasNext() abruft, haben Sie wahrscheinlich größere Probleme.

    
McDowell 01.02.2010 11:21
quelle
4

Wenn Ihre Aufrufe hasNext() und next() nicht in einer synchronisierten Block / Methode sind, ist es nicht garantiert, dass Sie Elemente haben, selbst wenn Sie hasNext() vor next() aufrufen.

Der Vertrag der Schnittstelle Iterator besagt, dass NoSuchElementException ausgelöst werden sollte, wenn keine weiteren Elemente vorhanden sind. Fahren Sie mit der Methode next() fort, bis eine solche Ausnahme auftritt.

Sehen Sie sich dazu die java.util.concurrent an. Paket - es hat gleichzeitige Sammlungen, deren Iteratoren Ihnen helfen können - dh Sie können diese Sammlungen und Iteratoren verwenden anstatt Ihre eigenen zu implementieren.

    
Bozho 01.02.2010 11:18
quelle
2

Ich würde eher eine Ausnahme von next() werfen, wenn es keine Elemente mehr gibt. In einer Multithread-Umgebung ist hasNext() sowieso ziemlich nutzlos.

    
Joonas Pulakka 01.02.2010 11:19
quelle
2

Sie können die Schnittstelle Iterator selbst implementieren (falls Sie eine Schnittstelle mit einer anderen API benötigen) und Ihre Kunden auffordern, Ihre eigene strengere Schnittstelle zu implementieren.

Ich habe eine solche Klasse in meiner funktionalen Programmierbibliothek erstellt, um einfach ein Iterator um ein ResultSet in einem Projekt bei der Arbeit zu implementieren.

Meine Klasse EasierIterator implementiert die Iterator-Schnittstelle bei Bedarf dass der Client eine einfachere Schnittstelle basierend auf moveNext() / getCurrent() implementiert. Es macht tatsächlich das Zwischenspeichern des aktuellen Artikels für Sie.

    
Vincent Robert 10.02.2010 00:08
quelle
1

Sie könnten etwas Ähnliches wie das Folgende tun, wobei Sie den zugrunde liegenden Datenabruf an eine private Methode delegieren und hasNext() und next() implementieren, um unterschiedlich auf die Abwesenheit von Daten zu reagieren. Dies hat den Vorteil, dass Sie next() immer wieder aufrufen können, ohne zuerst hasNext() aufzurufen, und daher nicht gegen den Vertrag von Iterator verstößt.

%Vor%     
Adamski 01.02.2010 11:45
quelle
0

BEARBEITEN: In dieser Antwort habe ich versucht zu argumentieren, dass es erlaubt ist, worum die Frage geht. Aber ich habe einen Satz in der Iterator.hasNext -Dokumentation übersehen, der meine ganze Argumentation entwertet:

  

Mit anderen Worten, gibt true zurück, wenn next() ein Element zurückgibt und keine Ausnahme auslöst.

Dies deutet darauf hin, dass next wiederholt aufgerufen wird, bis hasNext wahr zurückgibt und als nächstes aufruft, bis ein NoSuchElementException die gleiche Sequenz von Elementen zurückgibt.

So scheint es, dass das, wonach die Frage fragt, nicht erlaubt ist.

Ursprüngliche Antwort

Dies ist ein Versuch, eine Anwaltsart zu spezifizieren. Aus Gründen der Klarheit werde ich die Frage in einer kompakten Form wiederholen:

  

Ist es erlaubt durch die Iterable Spezifikation eine NoSuchElementException zu werfen, wenn Iterator.next aufgerufen wird ohne einen vorhergehenden Aufruf von Iterator.hasNext , auch wenn ein Element zurückgegeben worden wäre, wenn Iterator.hasNext zuerst aufgerufen wurde?

Diskussion

Die Dokumentation zu Iterator.hasNext lautet :

  

Gibt true zurück, wenn die Iteration mehr Elemente enthält.

Und für Iterator.next :

  

Wirf: NoSuchElementException - wenn die Iteration keine Elemente mehr enthält

Anscheinend ist es erlaubt, NoSuchElementException zu werfen, wenn "die Iteration keine Elemente mehr hat", aber vorher nicht. Das sollte übereinstimmen, wenn hasNext false zurückgibt.

Daraus ergibt sich die Frage: Was genau bedeutet die Dokumentation mit "der Iteration" und "Elementen"? Die Iterator -Dokumentation gibt keine Antwort darauf, was den Implementierern etwas Spielraum lässt.

Aus meiner Sicht gibt es zwei mögliche Interpretationen:

  1. Aus der Perspektive der Iteratorschnittstelle selbst ist das einzige existierende Konzept von "Iteration" "solange hasNext wahr zurückgibt". Das heißt, wenn der Client next vor hasNext aufruft, wissen sie nicht, ob mehr Elemente vorhanden sind, undefiniert.

    Es ist daher durch die Spezifikation für den Iterator-Implementierer erlaubt zu entscheiden, dass die Iteration beendet ist. Also ist die Antwort auf die Frage ja.

  2. Aber die Dokumentation zu Iterable.iterator Erwähnen Sie auch "Elemente":

      

    Gibt einen Iterator über Elemente vom Typ T zurück.

    Was bedeutet "Elemente" hier? Bedeutet es "alle Elemente in der Sammlung, die Iterable implementieren? Nein, das sagt es nicht, und nicht alle iterierbaren Elemente haben eine feste Menge von Elementen.

    Was "Elemente" für bestimmte iterierbare Elemente bedeuten, bleibt dem Implementierer überlassen. Eine gültige Definition von "Elementen" für einen Iterator könnte sein: " alle Elemente in der Sammlung ODER alle Elemente, bevor der Client entscheidet, next vor hasNext " aufzurufen.

    Dieser Fall führt also auch zu der Schlussfolgerung, dass die Antwort auf die Frage ja lautet. (Aber bitte beachten Sie den Hinweis am Ende!)

Zusammenfassung

Die Dokumentation ist nicht wirklich klar, aber es scheint, als ob die Antwort auf die Frage lautet: Ja, das ist erlaubt.

Hinweis

Falls ein Iterator das tut, was die Frage zu diesem Verhalten stellt, sollte dies natürlich dokumentiert werden. Aber andere Iterables sollten auch dokumentieren, welche Elemente ihre Iteratoren sind.

Beispiel: ArrayList.iterator Die Dokumentation gibt eindeutig an, welche Elemente den Iterator durchlaufen:

  

Gibt einen Iterator über die Elemente in dieser Liste in der richtigen Reihenfolge zurück.

Schlussbemerkung: Ja, ich bin verrückt, so viel Zeit damit zu verbringen.

    
Lii 26.04.2016 14:11
quelle

Tags und Links