Pourquoi Iterator et ListIterator de Java pointent-ils entre les éléments?

9

Le Javadoc pour ListIterator dit:

A ListIteratorn'a aucun élément courant; sa position de curseur se situe toujours entre l'élément qui serait retourné par un appel à previous()et l'élément qui serait retourné par un appel à next().

Pourquoi Java a-t-il été ListIteratorimplémenté pour pointer entre les éléments plutôt que vers un élément actuel? Il semble que ce qui rend le code client moins lisible quand il doit appeler à plusieurs reprises getNext(), getPrevious()et je suppose qu'il doit y avoir une bonne raison pour le choix.

Comme une note de côté, je viens d' écrire une petite bibliothèque appelée peekable-arraylist qui se prolonge ArrayList, Iteratoret ListIteratorqui fournit une peekAtNext()et peekAtPrevious()méthodes mises en œuvre comme:

  @Override public synchronized T peekAtNext() {
     T t = next();
     previous();
     return t;
  }
glenviewjeff
la source
2
Il y a un itérateur visible dans la bibliothèque de goyave code.google.com/p/guava-libraries
kevin cline
Merci Kevin! J'ai posté une question de suivi à ce sujet sur SO: Est-il possible d'utiliser le ForwardingListIterator de Guava avec un PeekingIterator?
glenviewjeff

Réponses:

12

Pour autant que je sache, la raison peut être trouvée dans la partie de javadoc que vous n'avez pas citée (souligné ci-dessous le mien):

Un itérateur pour les listes qui permet au programmeur de parcourir la liste dans les deux sens, de modifier la liste pendant l'itération ...

Vous voyez, le but recherché est d'autoriser l'utilisation pendant la modification de la liste. Les modifications possibles incluent apparemment la suppression des éléments.

Pensez maintenant à ce qui se passerait si nous supprimions un élément qui serait current()pour l'itérateur - en supposant que l'itérateur aurait une notion d'élément actuel? Dans ce contexte, la façon de l'implémenter sans notion d' élément courant me semble assez logique - car de cette façon, l'itérateur n'a pas à se soucier de la suppression des éléments.


Il est important de noter que javadoc ne nécessite pas d'implémentations d'interface pour être thread-safe.

  • À cause de cela, il ne faut pas attendre la manipulation correcte des modifications effectuées à partir de différentes discussions - pour que la mise en œuvre devrait fournir des moyens supplémentaires pour l' accès Synchronize, garantie la visibilité etc comme spécifié par modèle mémoire de Java par JSR 133 .

Ce dont ListIterator est capable, c'est de gérer les modifications effectuées à partir du même thread lors de l'itération. Tous les itérateurs ne sont pas comme ça, les javadocs ConcurrentModificationException avertissent spécifiquement à ce sujet:

... Notez que cette exception n'indique pas toujours qu'un objet a été modifié simultanément par un thread différent. Si un seul thread émet une séquence d'appels de méthode qui viole le contrat d'un objet, l'objet peut lever cette exception. Par exemple, si un thread modifie une collection directement pendant qu'il itère sur la collection avec un itérateur à échec rapide, l'itérateur lèvera cette exception ...

moucheron
la source
1
Cela signifie également que vous n'avez pas besoin d'un système séparé insertBeforeet insertAfter, bien que ce ne soit pas un problème aussi important qu'un remove.
Karl Bielefeldt
1
Est-ce donc le problème que l'on pourrait simultanément supprimer et accéder à l'élément actuel? Ne serait-ce pas le problème équivalent à l'appel simultané next()? remove()serait synchronizedcomme le ferait getCurrent(). Suis-je en train de manquer quelque chose?
glenviewjeff
@glenviewjeff c'est une très bonne observation. Je me demande également comment le CME pourrait être géré là-bas - l'intention déclarée de permettre des modifications soulève de telles préoccupations. Je suppose que je dois creuser plus profondément. Je suis sûr à 99,99% qu'il n'est pas conçu pour être sûr pour les threads, peut-être qu'il ne peut gérer que les mods simultanés effectués à partir du même thread (tous les itérateurs ne sont pas capables de cela, vous savez)
gnat
Si vous êtes intéressé, consultez ma modification. J'ai implémenté et packagé une extension pour ArrayListy parvenir.
glenviewjeff
@glenviewjeff intéressant. Dans votre implémentation, est-ce que next () et previous () sont également synchronisés? Je demande parce que sinon, un autre thread peut "percer" un mouvement inattendu au milieu de votre courant (), ce qui ne le fait pas tout à fait comme le client l'attendrait ... Des méthodes comme add () auraient probablement besoin de synchronisation aussi
gnat