Dlaczego Iterator i ListIterator Java wskazują między elementami?

9

Javadoc dla ListIterator mówi:

A ListIteratornie ma bieżącego elementu; jego pozycja kursora zawsze znajduje się między elementem, który zostałby zwrócony przez wywołanie, previous()a elementem, który zostałby zwrócony przez wywołanie do next().

Dlaczego Java została ListIteratorzaimplementowana tak, aby wskazywała między elementami, a nie na bieżący element? Wydaje się, że to sprawia, że kod klienta mniej czytelny, gdy ma się wielokrotnie zadzwonić getNext(), getPrevious(), więc zakładam, że musi być dobrym powodem wyboru.

Na marginesie, ja tylko napisałem trochę biblioteka o nazwie peekable-ArrayList , która rozciąga się ArrayList, Iteratori ListIteratorktóra zapewnia peekAtNext()i peekAtPrevious()wdrożonych metod, takich jak:

  @Override public synchronized T peekAtNext() {
     T t = next();
     previous();
     return t;
  }
glenviewjeff
źródło
2
Jest peekable iterator w bibliotece Guava code.google.com/p/guava-libraries
Kevin Cline
Dzięki, Kevin! Wysłałem
glenviewjeff

Odpowiedzi:

12

O ile mi wiadomo, przyczynę można znaleźć w części javadoc, której nie zacytowałeś (podkreślenie poniżej mojej):

Iterator list, który pozwala programiście przechodzić przez listę w dowolnym kierunku, modyfikować listę podczas iteracji ...

Widzisz, zamierzonym celem jest umożliwienie użycia podczas modyfikowania listy. Możliwe modyfikacje najwyraźniej obejmują usunięcie elementów.

Pomyśl teraz, co by się stało, gdybyśmy usunęli element, który byłby current()dla iteratora - zakładając, że iterator miałby pojęcie elementu bieżącego? W tym kontekście sposób implementacji bez pojęcia bieżącego elementu ma dla mnie całkiem dobry sens - ponieważ w ten sposób iterator nie musi się martwić o usunięcie elementów.


Jest ważne , aby pamiętać, że javadoc nie wymaga implementacji interfejsu być bezpieczne dla wątków.

  • Z tego powodu nie należy oczekiwać poprawnej obsługi modyfikacji wykonanych z różnych wątków - w tym celu implementacja musiałaby zapewnić dodatkowe środki do synchronizacji dostępu, zagwarantować widoczność itp. Zgodnie z modelem Java Memory Model według JSR 133 .

ListIterator jest w stanie obsłużyć modyfikacje wykonane z tego samego wątku podczas iteracji. Nie wszystkie iteratory są takie, javadocs ConcurrentModificationException ostrzegają o tym:

... Zwróć uwagę, że ten wyjątek nie zawsze oznacza, że ​​obiekt został jednocześnie zmodyfikowany przez inny wątek. Jeśli pojedynczy wątek generuje sekwencję wywołań metody, która narusza kontrakt obiektu, obiekt może zgłosić ten wyjątek. Na przykład, jeśli wątek modyfikuje kolekcję bezpośrednio podczas iteracji nad kolekcją za pomocą niezawodnego iteratora, iterator zgłosi ten wyjątek ...

komar
źródło
1
Oznacza to również, że nie potrzebujesz osobnego insertBeforei insertAfterchociaż nie jest to tak duży problem jak remove.
Karl Bielefeldt
1
Czy jest więc problem, który można jednocześnie usunąć i uzyskać dostęp do bieżącego elementu? Czy nie byłby to równoważny problem z jednoczesnym dzwonieniem next()? remove()byłoby tak synchronizedjak by to było getCurrent(). Czy coś brakuje?
glenviewjeff
@glenviewjeff to bardzo dobra obserwacja. Zastanawiam się również, jak można tam sobie poradzić z CME - bardzo wyraźny zamiar dopuszczenia modyfikacji budzi takie obawy. Chyba muszę kopać głębiej. Jestem 99,99% pewien, że to nie są przeznaczone jako bezpieczne dla wątków, może obsługiwać tylko jednoczesnych modów zrobione z tego samego wątku (nie wszystkie iteratory są zdolne, że wiesz)
komara
Jeśli jesteś zainteresowany, zobacz moją edycję. W tym celu zaimplementowałem i spakowałem rozszerzenie ArrayList.
glenviewjeff
@glenviewjeff ciekawe. Czy w twojej implementacji next () i previous () również są zsynchronizowane? Pytam, bo jeśli nie, to inny wątek może „wywiercić” jakiś nieoczekiwany ruch bezpośrednio na środek twojej bieżącej (), sprawiając, że zachowuje się niezupełnie tak, jak oczekiwałby klient ... Metody takie jak add () prawdopodobnie również wymagają synchronizacji
komara