Pourquoi l' Iterator
interface ne s'étend pasIterable
?
La iterator()
méthode pourrait simplement retourner this
.
Est-ce intentionnel ou simplement une erreur des concepteurs de Java?
Il serait pratique de pouvoir utiliser une boucle for-each avec des itérateurs comme ceci:
for(Object o : someContainer.listSomeObjects()) {
....
}
où listSomeObjects()
renvoie un itérateur.
Réponses:
Parce qu'un itérateur pointe généralement vers une seule instance dans une collection. Iterable implique que l'on peut obtenir un itérateur à partir d'un objet pour traverser ses éléments - et il n'est pas nécessaire d'itérer sur une seule instance, ce que représente un itérateur.
la source
Un itérateur est avec état. L'idée est que si vous appelez
Iterable.iterator()
deux fois, vous obtiendrez des itérateurs indépendants - du moins pour la plupart des itérables. Ce ne serait clairement pas le cas dans votre scénario.Par exemple, je peux généralement écrire:
Cela devrait imprimer la collection deux fois - mais avec votre schéma, la deuxième boucle se terminerait toujours instantanément.
la source
iterator
et utilisez le résultat, il doit itérer sur la collection - ce qu'il ne fera pas si ce même objet a déjà itéré sur la collection. Pouvez-vous donner une implémentation correcte (autre que pour une collection vide) où le même itérateur est retourné deux fois?iterable
deux fois vous donnera des itérateurs indépendants.Pour mon 0,02 $, je suis tout à fait d'accord pour dire qu'Iterator ne devrait pas implémenter Iterable, mais je pense que la boucle for améliorée devrait non plus accepter. Je pense que tout l'argument «rendre les itérateurs itérables» apparaît comme une solution à un défaut de langage.
La seule raison de l'introduction de la boucle for améliorée était qu'elle "élimine la corvée et la propension aux erreurs des itérateurs et des variables d'index lors de l'itération sur des collections et des tableaux" [ 1 ].
Pourquoi alors ce même argument ne vaut-il pas pour les itérateurs?
Dans les deux cas, les appels à hasNext () et next () ont été supprimés et il n'y a aucune référence à l'itérateur dans la boucle interne. Oui, je comprends que les Iterables peuvent être réutilisés pour créer plusieurs itérateurs, mais que tout se passe en dehors de la boucle for: à l'intérieur de la boucle, il n'y a jamais de progression vers l'avant, un élément à la fois, sur les éléments retournés par l'itérateur.
En outre, autoriser cela faciliterait également l'utilisation de la boucle for pour les énumérations, qui, comme cela a été souligné ailleurs, sont analogues aux itérateurs et non aux itérables.
Donc, ne laissez pas Iterator implémenter Iterable, mais mettez à jour la boucle for pour accepter non plus.
À votre santé,
la source
for(item : iter) {...}
syntaxe, alors il provoquera une erreur lorsque le même itérateur sera itéré deux fois. Imaginez que celaIterator
soit passé dans laiterateOver
méthode plutôt queIterable
dans cet exemple .for (String x : strings) {...}
ouwhile (strings.hasNext()) {...}
: si vous essayez de parcourir un itérateur deux fois la deuxième fois, cela ne donnera aucun résultat, donc je ne vois pas cela en soi comme un argument contre l'autorisation de la syntaxe améliorée. La réponse de Jon est différente, car là, il montre à quel point l'enveloppement d'unIterator
dans unIterable
causerait des problèmes, car dans ce cas, vous vous attendriez à pouvoir le réutiliser autant de fois que vous le souhaitez.Comme l'ont souligné d'autres,
Iterator
et ceIterable
sont deux choses différentes.De plus, les
Iterator
implémentations sont antérieures aux boucles for améliorées.Il est également trivial de surmonter cette limitation avec une méthode d'adaptateur simple qui ressemble à ceci lorsqu'elle est utilisée avec des importations de méthode statique:
Exemple d'implémentation:
Dans Java 8, adapter un
Iterator
à unIterable
devient plus simple:la source
for (String s : (Iterable<String>) () -> iterator)
Comme d'autres l'ont dit, un Iterable peut être appelé plusieurs fois, renvoyant un nouvel Iterator à chaque appel; un itérateur n'est utilisé qu'une seule fois. Ils sont donc liés, mais servent des objectifs différents. Malheureusement, la méthode «compact pour» ne fonctionne qu'avec un itérable.
Ce que je décrirai ci-dessous est une façon d'avoir le meilleur des deux mondes - renvoyer un Iterable (pour une syntaxe plus agréable) même lorsque la séquence de données sous-jacente est unique.
L'astuce consiste à renvoyer une implémentation anonyme de l'itérable qui déclenche réellement le travail. Ainsi, au lieu de faire le travail qui génère une séquence ponctuelle puis de renvoyer un Iterator dessus, vous retournez un Iterable qui, chaque fois qu'il est accédé, refait le travail. Cela peut sembler un gaspillage, mais souvent vous n'appelerez l'itérable qu'une seule fois de toute façon, et même si vous l'appelez plusieurs fois, il a toujours une sémantique raisonnable (contrairement à un simple wrapper qui fait qu'un Iterator "ressemble" à un Iterable, cela a gagné ' t échouer s'il est utilisé deux fois).
Par exemple, disons que j'ai un DAO qui fournit une série d'objets à partir d'une base de données, et que je veux donner accès à cela via un itérateur (par exemple pour éviter de créer tous les objets en mémoire s'ils ne sont pas nécessaires). Maintenant, je pourrais simplement retourner un itérateur, mais cela rend l'utilisation de la valeur retournée dans une boucle moche. Donc, à la place, j'emballe tout dans un Iterable anon:
cela peut ensuite être utilisé dans un code comme celui-ci:
ce qui me permet d'utiliser la boucle for compacte tout en conservant l'utilisation de la mémoire incrémentielle.
Cette approche est «paresseuse» - le travail n'est pas fait lorsque l'itérable est demandé, mais seulement plus tard lorsque le contenu est répété - et vous devez être conscient des conséquences de cela. Dans l'exemple avec un DAO, cela signifie itérer sur les résultats dans la transaction de base de données.
Il y a donc plusieurs mises en garde, mais cela peut encore être un idiome utile dans de nombreux cas.
la source
returning a fresh Iterator on each call
devrait être accompagné de pourquoi c'est à dire pour éviter les problèmes de concurrence ...Incroyablement, personne d'autre n'a encore donné cette réponse. Voici comment vous pouvez "facilement" parcourir un
Iterator
en utilisant la nouvelleIterator.forEachRemaining()
méthode Java 8 :Bien sûr, il existe une solution "plus simple" qui fonctionne directement avec la boucle foreach, enveloppant le
Iterator
dans unIterable
lambda:la source
Iterator
est une interface qui vous permet de parcourir quelque chose. C'est une implémentation de se déplacer dans une collection quelconque.Iterable
est une interface fonctionnelle qui indique que quelque chose contient un itérateur accessible.En Java8, cela vous facilite la vie ... Si vous avez un
Iterator
mais que vous en avez besoin,Iterable
vous pouvez simplement faire:Cela fonctionne également dans la boucle for:
la source
Je suis d'accord avec la réponse acceptée, mais je veux ajouter ma propre explication.
L'itérateur représente l'état de la traversée, par exemple, vous pouvez obtenir l'élément courant d'un itérateur et passer au suivant.
Iterable représente une collection qui pourrait être parcourue, il peut renvoyer autant d'itérateurs que vous le souhaitez, chacun représentant son propre état de parcours, un itérateur peut pointer vers le premier élément, tandis qu'un autre peut pointer vers le 3ème élément.
Ce serait bien si Java for loop accepte à la fois Iterator et Iterable.
la source
Pour éviter de dépendre du
java.util
packageSelon le JSR original, une boucle for améliorée pour le langage de programmation Java ™ , les interfaces proposées:
java.lang.Iterable
java.lang.ReadOnlyIterator
(proposé d'être modernisé
java.util.Iterator
, mais apparemment cela ne s'est jamais produit)… Ont été conçus pour utiliser l'
java.lang
espace de noms du package plutôt quejava.util
.Pour citer le JSR:
À propos, l'ancien a
java.util.Iterable
acquis une nouvelleforEach
méthode en Java 8+, à utiliser avec la syntaxe lambda (en passant aConsumer
).Voici un exemple. L'
List
interface étend l'Iterable
interface, car toute liste porte uneforEach
méthode.la source
J'en vois aussi beaucoup faire ceci:
Mais cela ne règle pas les choses! Cette méthode ne serait pas ce que vous voulez!
La méthode
iterator()
est censée renvoyer un nouvel itérateur à partir de zéro. Il faut donc faire quelque chose comme ça:La question est: y aurait-il un moyen de faire en sorte qu'une classe abstraite exécute cela? Donc, pour obtenir un IterableIterator, il suffit d'implémenter les deux méthodes next () et hasNext ()
la source
Si vous êtes venu ici à la recherche d'une solution de contournement, vous pouvez utiliser IteratorIterable . (disponible pour Java 1.6 et supérieur)
Exemple d'utilisation (inverser un vecteur).
impressions
la source
Par souci de simplicité, Iterator et Iterable sont deux concepts distincts, Iterable est simplement un raccourci pour «Je peux renvoyer un Iterator». Je pense que votre code devrait être:
avec someContainer instanceof
SomeContainer extends Iterable<Object>
la source
En passant: Scala a une méthode toIterable () dans Iterator. Voir la conversion implicite ou explicite de scala d'itérateur en itérable
la source
Sur une note connexe, vous pouvez trouver l'adaptateur IteratorIterable dans Apache Commons Collections4 utile. Créez simplement une instance à partir d'un itérateur et vous avez l'itérable correspondant.
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html
ID: org.apache.commons: commons-collections4: 4.0
la source
Les itérateurs sont avec état, ils ont un élément "suivant" et deviennent "épuisés" une fois itérés. Pour voir où est le problème, exécutez le code suivant, combien de nombres sont imprimés?
la source
Vous pouvez essayer l'exemple suivant:
la source