Warum zeigen Javas Iterator und ListIterator zwischen Elementen?

9

Der Javadoc für ListIterator sagt:

A ListIteratorhat kein aktuelles Element; Die Cursorposition liegt immer zwischen dem Element, das bei einem Aufruf von zurückgegeben wird, previous()und dem Element, das bei einem Aufruf von zurückgegeben wird next().

Warum wurde Java so ListIteratorimplementiert, dass es zwischen Elementen und nicht auf ein aktuelles Element zeigt? Es scheint, dass dies den Client-Code weniger lesbar macht, wenn er wiederholt aufgerufen getNext()werden getPrevious()muss. Daher gehe ich davon aus, dass es einen guten Grund für die Wahl geben muss.

Als Randbemerkung, schrieb ich nur eine kleine Bibliothek peekable-Arraylist genannt , die sich ArrayList, Iteratorund ListIteratordas stellt ein peekAtNext()und peekAtPrevious()Verfahren implementiert , wie:

  @Override public synchronized T peekAtNext() {
     T t = next();
     previous();
     return t;
  }
glenviewjeff
quelle
2
Es gibt einen spähbaren Iterator in der Guava-Bibliothek code.google.com/p/guava-libraries
Kevin Cline
Danke Kevin! Ich habe auf SO eine Folgefrage dazu gestellt: Ist es möglich, Guavas ForwardingListIterator mit einem PeekingIterator zu verwenden?
Glenviewjeff

Antworten:

12

Soweit ich das beurteilen kann, liegt der Grund in dem Teil von Javadoc, den Sie nicht zitiert haben (Hervorhebung unter meinem):

Ein Iterator für Listen, mit dem der Programmierer die Liste in beide Richtungen durchlaufen und die Liste während der Iteration ändern kann ...

Sie sehen, der beabsichtigte Zweck besteht darin, die Verwendung zuzulassen, während die Liste geändert wird. Mögliche Änderungen umfassen offenbar das Entfernen der Elemente.

Überlegen Sie nun, was passieren würde, wenn wir ein Element entfernen würden, das current()für den Iterator bestimmt ist - vorausgesetzt, der Iterator hätte eine Vorstellung vom aktuellen Element? In diesem Zusammenhang ist die Art und Weise, wie es ohne den Begriff des aktuellen Elements implementiert wird, für mich ziemlich sinnvoll - da sich der Iterator auf diese Weise nicht um das Entfernen von Elementen kümmern muss.


Es ist wichtig zu beachten, dass für javadoc keine Schnittstellenimplementierungen erforderlich sind, um threadsicher zu sein.

  • Aus diesem Grund sollte man nicht erwarten, dass Änderungen , die von verschiedenen Threads vorgenommen wurden, korrekt behandelt werden. Dazu müsste die Implementierung zusätzliche Mittel bereitstellen, um den Zugriff zu synchronisieren, die Sichtbarkeit zu gewährleisten usw., wie im Java Memory Model gemäß JSR 133 angegeben .

ListIterator ist in der Lage, Änderungen zu verarbeiten, die beim Iterieren von demselben Thread vorgenommen wurden. Nicht alle Iteratoren sind so, ConcurrentModificationException-Javadocs warnen ausdrücklich davor:

... Beachten Sie, dass diese Ausnahme nicht immer anzeigt, dass ein Objekt gleichzeitig von einem anderen Thread geändert wurde. Wenn ein einzelner Thread eine Folge von Methodenaufrufen ausgibt, die gegen den Vertrag eines Objekts verstoßen, kann das Objekt diese Ausnahme auslösen. Wenn ein Thread beispielsweise eine Sammlung direkt ändert, während er mit einem ausfallsicheren Iterator über die Sammlung iteriert, löst der Iterator diese Ausnahme aus ...

Mücke
quelle
1
Es bedeutet auch, dass Sie kein separates insertBeforeund benötigen insertAfter, obwohl dies kein so großes Problem ist wie ein remove.
Karl Bielefeldt
1
Ist das Problem also, dass man gleichzeitig das aktuelle Element entfernen und darauf zugreifen kann? Wäre dies nicht das gleiche Problem wie das gleichzeitige Anrufen next()? remove()wäre synchronizedwie wäre getCurrent(). Vermisse ich etwas
Glenviewjeff
@glenviewjeff das ist eine sehr gute Beobachtung. Ich frage mich auch, wie CME dort gehandhabt werden könnte - die sehr erklärte Absicht, Änderungen zuzulassen, wirft solche Bedenken auf. Ich denke, ich muss tiefer graben. Ich bin mir zu 99,99% sicher, dass es nicht als threadsicher gedacht ist. Vielleicht kann es nur gleichzeitige Mods verarbeiten, die von demselben Thread ausgeführt wurden (nicht alle Iteratoren sind dazu in der Lage, wissen Sie)
Mücke
Wenn Sie interessiert sind, sehen Sie meine Bearbeitung. Ich habe eine Erweiterung implementiert und verpackt ArrayList, um dies zu erreichen.
Glenviewjeff
@glenviewjeff interessant. Werden in Ihrer Implementierung auch next () und previous () synchronisiert? Ich frage, denn wenn nicht, kann ein anderer Thread eine unerwartete Bewegung direkt in die Mitte Ihres aktuellen () "bohren", sodass er sich nicht ganz so verhält, wie es der Client erwarten würde ... Methoden wie add () müssten wahrscheinlich auch synchronisiert werden
Mücke