Java: pourquoi ne pas pouvoir parcourir un iterator?

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:

    1. 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é.
    2. 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).
    3. Il y a une solution de contournement facile . Il peut sembler un peu inutile d’allouer un nouvel object rien que pour cela, mais l’affectation n’est pas chère et l’parsing par échappatoire vous débarrasserait même de ce petit coût dans la plupart des cas. (Pourquoi ils n’ont pas inclus cette solution de contournement dans une classe d’utilitaire Iterables , analogue à Collections et Arrays , me dépasse cependant.)
    4. (Probablement pas vrai – voir les commentaires). Il me semble rappeler que le JLS ne peut que référencer des choses dans java.lang [citation requirejse] , donc ils devraient créer une interface Iterator dans java.lang qui java.util.Iterator s’étend sans rien append à. Maintenant, nous avons deux interfaces itératives fonctionnellement équivalentes. 50% du nouveau code utilisant des iterators nus choisiront la version java.lang , les autres utiliseront celle de java.util . Le chaos s’ensuit, les problèmes de compatibilité ne manquent pas, etc.

    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 n’implémente-t-il pas Iterable ?

    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.