Perché Iterator e ListIterator di Java puntano tra gli elementi?


9

Il Javadoc per ListIterator dice:

A ListIteratornon ha alcun elemento corrente; la sua posizione del cursore si trova sempre tra l'elemento a cui verrebbe restituito una chiamata previous()e l'elemento a cui verrebbe restituito una chiamata next().

Perché Java è stato ListIteratorimplementato per puntare tra gli elementi piuttosto che verso un elemento corrente? Sembra che questo rende il codice client meno leggibile quando deve chiamare più volte getNext(), getPrevious(), quindi immagino ci deve essere un motivo buono per la scelta.

Come nota a margine, ho appena scritto una piccola libreria chiamata peekable-arraylist che si estende ArrayList, Iteratore ListIteratorche fornisce un peekAtNext()e peekAtPrevious()metodi implementati come:

  @Override public synchronized T peekAtNext() {
     T t = next();
     previous();
     return t;
  }

2
C'è un iteratore sbirciabile nella libreria di Guava code.google.com/p/guava-libraries
kevin cline

Grazie Kevin! Ho inviato una domanda di follow-up a questo su SO: è possibile utilizzare ForwardingListIterator di Guava con un PeekingIterator?
glenviewjeff,

Risposte:


12

Per quanto ne so, la ragione può essere trovata nella parte di javadoc che non hai citato (enfasi sotto la mia):

Un iteratore per elenchi che consente al programmatore di attraversare l'elenco in entrambe le direzioni, modificare l'elenco durante l'iterazione ...

Vedete, lo scopo previsto è consentire l'utilizzo mentre l'elenco viene modificato. Le possibili modifiche apparentemente includono la rimozione degli elementi.

Ora pensa a cosa accadrebbe se rimuovessimo un elemento che sarebbe current()per iteratore - supponendo che iteratore avrebbe una nozione di elemento corrente? In questo contesto, il modo di implementarlo senza una nozione di elemento corrente ha molto senso per me - perché in questo modo, l'iteratore non deve preoccuparsi della rimozione degli elementi.


È importante notare che javadoc non richiede che le implementazioni dell'interfaccia siano thread-safe.

  • Per questo motivo, non ci si dovrebbe aspettare la corretta gestione delle modifiche apportate da diversi thread - per questo, l'implementazione dovrebbe fornire mezzi aggiuntivi per sincronizzare l'accesso, garantire la visibilità ecc. Come specificato da Java Memory Model per JSR 133 .

Ciò di cui ListIterator è capace è la gestione delle modifiche apportate dallo stesso thread durante l'iterazione. Non tutti gli iteratori sono così, ConcurrentModificationException javadocs avvertono specificamente di questo:

... Nota che questa eccezione non indica sempre che un oggetto è stato modificato contemporaneamente da un thread diverso. Se un singolo thread emette una sequenza di invocazioni di metodi che violano il contratto di un oggetto, l'oggetto può generare questa eccezione. Ad esempio, se un thread modifica direttamente una raccolta mentre sta iterando sulla raccolta con un iteratore fail-fast, l'iteratore genererà questa eccezione ...


1
Significa anche che non hai bisogno di un separato insertBeforee insertAfter, sebbene non sia un grosso problema come un remove.
Karl Bielefeldt,

1
Quindi è il problema che si potrebbe rimuovere e accedere contemporaneamente all'elemento corrente? Non sarebbe questo il problema equivalente come chiamare contemporaneamente next()? remove()sarebbe synchronizedcome sarebbe getCurrent(). Mi sto perdendo qualcosa?
glenviewjeff

@glenviewjeff che è una molto buona osservazione. Mi chiedo anche come il CME possa essere gestito lì - l'intenzione dichiarata di consentire le modifiche solleva preoccupazioni del genere. Immagino di dover scavare più a fondo. Sono sicuro al 99,99% che non è inteso come thread-safe, forse può solo gestire mod simultanee fatte dallo stesso thread (non tutti gli iteratori sono in grado di farlo, lo sai)
moscerino

Se sei interessato, vedi la mia modifica. Ho implementato e impacchettato un'estensione ArrayListper raggiungere questo obiettivo.
glenviewjeff

@glenviewjeff interessante. Nella tua implementazione, sono sincronizzati anche next () e previous ()? Lo chiedo perché, in caso contrario, altri thread possono "eseguire" alcune mosse inaspettate nel bel mezzo del tuo attuale (), facendolo comportare non proprio come il cliente si aspetterebbe ... Metodi come add () probabilmente avrebbero bisogno anche di sincronizzazione
moscerino
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.