Por que o Iterator e o ListIterator do Java apontam entre elementos?


9

O Javadoc para ListIterator diz:

A ListIteratornão possui elemento atual; sua posição do cursor sempre fica entre o elemento que seria retornado por uma chamada previous()e o elemento que seria retornado por uma chamada para next().

Por que o Java foi ListIteratorimplementado para apontar entre elementos e não para um elemento atual? Parece que isso faz com que o código do cliente menos legível quando se tem que chamar várias vezes getNext(), getPrevious(), então eu suponho que deve haver uma boa razão para a escolha.

Como uma nota lateral, eu só escrevi uma pequena biblioteca chamada peekable-matrizes que se estende ArrayList, Iteratore ListIteratorque fornece um peekAtNext()e peekAtPrevious()métodos implementados como:

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

2
Existe um iterador agradável na biblioteca do Guava code.google.com/p/guava-libraries
kevin cline

Obrigado Kevin! Postei uma pergunta de acompanhamento no SO: É possível usar o ForwardingListIterator do Guava com um PeekingIterator?
glenviewjeff

Respostas:


12

Tanto quanto posso dizer, o motivo pode ser encontrado na parte do javadoc que você não citou (ênfase abaixo da minha):

Um iterador para listas que permite ao programador percorrer a lista em qualquer direção, modificar a lista durante a iteração ...

Veja, o objetivo pretendido é permitir o uso enquanto a lista estiver sendo modificada. As possíveis modificações aparentemente incluem a remoção dos elementos.

Agora pense no que aconteceria se removêssemos um elemento que seria current()para o iterador - supondo que o iterador tivesse uma noção do elemento atual? Nesse contexto, a maneira de implementá-lo sem a noção de elemento atual faz muito sentido para mim - porque, dessa forma, o iterador não precisa se preocupar com a remoção de elementos.


É importante observar que o javadoc não requer que as implementações de interface sejam seguras contra threads.

  • Por esse motivo, não se deve esperar o manuseio correto das modificações feitas a partir de diferentes threads - para isso, a implementação teria que fornecer meios adicionais para sincronizar o acesso, garantir visibilidade etc., conforme especificado pelo Java Memory Model por JSR 133 .

O ListIterator é capaz de lidar com modificações feitas a partir do mesmo thread durante a iteração. Nem todos os iteradores são assim, os javadocs ConcurrentModificationException alertam especificamente sobre isso:

... Observe que essa exceção nem sempre indica que um objeto foi modificado simultaneamente por um thread diferente. Se um único encadeamento emitir uma sequência de invocações de método que viole o contrato de um objeto, o objeto poderá lançar esta exceção. Por exemplo, se um encadeamento modificar uma coleção diretamente enquanto estiver iterando sobre a coleção com um iterador à prova de falhas, o iterador lançará esta exceção ...


11
Isso também significa que você não precisa de um separado insertBeforee insertAfter, embora isso não seja um problema tão grande quanto um remove.
Karl Bielefeldt

11
Então, é o problema que se pode remover e acessar simultaneamente o elemento atual? Este não seria o problema equivalente ao mesmo tempo em que chamava next()? remove()seria synchronizedcomo seria getCurrent(). Estou esquecendo de algo?
precisa

@glenviewjeff é uma observação muito boa. Também me pergunto como o CME poderia ser tratado lá - a intenção declarada de permitir modificações suscita preocupações como essa. Acho que preciso cavar mais fundo. Estou 99,99% de certeza que não é concebido como segmento seguro, talvez ele só pode lidar com mods simultâneas feitas a partir do mesmo thread (nem todos os iteradores são capazes de que, você sabe)
mosquito

Se você estiver interessado, veja minha edição. Eu implementei e empacotei uma extensão ArrayListpara fazer isso.
glenviewjeff

@glenviewjeff interesting. Na sua implementação, next () e previous () também são sincronizados? Eu pergunto porque, se não, então outro thread pode "perfurar" algum movimento inesperado no meio do seu atual (), fazendo com que ele não se comporte exatamente como o cliente esperaria ... Métodos como o add () provavelmente precisariam de sincronização também
gnat
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.