J’ai lu Pourquoi l’iterator de Java n’est-il pas une Iterable? et pourquoi les énumérations ne sont-elles pas interchangeables? , mais je ne comprends toujours pas pourquoi:
void foo(Iterator it) { for (X x : it) { bar(x); baz(x); } }
n’a pas été rendu possible. En d’autres termes, à moins de manquer quelque chose, ce qui précède aurait pu être un bon sucre syntaxique valable pour:
void foo(Iterator it) { for (X x; it.hasNext();) { x = it.next(); bar(x); baz(x); } }
mais je ne comprends toujours pas pourquoi cela n’a […] pas été rendu possible.
Je peux voir plusieurs raisons:
Iterator
ne sont pas réutilisables, donc un / chacun consum l’iterator – pas un comportement incorrect, peut-être, mais peu intuitif pour ceux qui ne savent pas comment le / chaque est désabusé. Iterator
n’apparaissent pas “nus” dans le code si souvent que cela compliquerait le JLS avec peu de gain (le pour / chaque construction est déjà assez mauvais, travaillant à la fois sur Iterable
et sur les tableaux). Iterables
, analogue à Collections
et Arrays
, me dépasse cependant.) java.lang
[citation requirejse] , donc ils devraient créer une interface Iterator
dans java.lang
qui java.util.Iterator
s’étend sans rien append à.java.lang
, les autres utiliseront celle de java.util
.Je pense que les points 1 à 3 correspondent à la manière dont la philosophie de conception du langage Java semble aller: ne surprenez pas les nouveaux venus, ne compliquez pas les spécifications si elle ne présente pas un gain évident Ne faites pas avec une langue ce qui peut être fait avec une bibliothèque.
Les mêmes arguments expliqueraient pourquoi java.util.Enumeration
n’est pas non plus Iterable
.
La raison en est probablement que les iterators ne sont pas réutilisables. Vous devez obtenir un nouvel iterator à partir de la collection Iterable chaque fois que vous souhaitez effectuer une itération sur les éléments. Cependant, comme solution rapide:
private static Iterable iterable(final Iterator it){ return new Iterable (){ public Iterator iterator(){ return it; } }; } //.... { // ... // Now we can use: for ( X x : iterable(it) ){ // do something with x } // ... } //....
Cela dit, la meilleure chose à faire est de faire circuler l’interface Iterable
au lieu de l’ Iterator
La syntaxe for(Type t : iterable)
n’est valide que pour les classes qui implémentent Iterable
.
Un iterator n’implémente pas les itérables.
Vous pouvez effectuer une itération sur des éléments tels que Collection
, List
ou Set
car ils implémentent Iterable.
Le code suivant est équivalent:
for (Type t: list) { // do something with t }
et
Iterator iter = list.iterator(); while (iter.hasNext()) { t = iter.next(); // do something with t }
La raison pour laquelle cela n’a pas été possible est que la syntaxe for-each a été ajoutée au langage pour extraire l’ Iterator
. Faire fonctionner la boucle for-each avec des iterators n’aboutirait pas à la création de la boucle for-each.
En fait, vous pouvez.
Il existe une solution de contournement très courte disponible sur java 8:
for (X item : (Iterable) () -> iterator)
Voir Comment itérer avec la boucle foreach sur le stream java 8 pour l’explication détaillée de l’astuce.
Et quelques explications pour expliquer pourquoi cela n’a pas été supporté nativement peuvent être trouvées dans la question connexe:
Pourquoi Stream
Les iterators ne sont pas destinés à être réutilisés (c’est-à-dire: utilisés dans plus d’une boucle d’itération). En particulier, Iterator.hasNext()
garantit que vous pouvez appeler Iterator.next()
toute sécurité et obtenir la valeur suivante de la collection sous-jacente.
Lorsque le même iterator est utilisé dans deux itérations simultanées (supposons un scénario multi-threading), cette promesse ne peut plus être tenue:
while(iter.hasNext() { // Now a context switch happens, another thread is performing // iter.hasNext(); x = iter.next(); Ssortingng s = iter.next(); // A runtime exception is thrown because the iterator was // exhausted by the other thread }
De tels scénarios violent complètement le protocole proposé par Iterator. En fait, ils peuvent se produire même dans un seul programme threadé: une boucle d’itération appelle une autre méthode qui utilise le même iterator pour effectuer sa propre itération. Lorsque cette méthode est renvoyée, l’appelant émet un appel Iterator.next()
qui, à nouveau, échoue.
Parce que le for-each est conçu pour se lire comme suit:
for each element of [some collection of elements]
Un Iterator
n’est pas [some collection of elements]
. Un tableau et une Iterable
est.