Quelle est la relation entre Iterable et Iterator?

Quelle est la différence entre Iterator et Iterable in Scala?

Je pensais que Iterable représente un ensemble que je peux parcourir et Iterator est un “pointeur” sur l’un des éléments de l’itération.

Cependant, Iterator a des fonctions comme forEach , map , foldLeft . Il peut être converti en Iterable via toIterable . Et, par exemple, scala.io.Source.getLines renvoie Iterator , non Iterable .

Mais je ne peux pas faire de groupBy sur Iterator et je peux le faire sur Iterable .

Alors, quelle est la relation entre ces deux, Iterator et Iterable ?

En bref: un Iterator a un état, alors qu’une Iterable ne le fait pas.

Voir les documents API pour les deux.

Iterable :

Un trait de base pour les collections itérables.

Ceci est un trait de base pour toutes les collections Scala qui définissent une méthode itérative pour parcourir les éléments de la collection un par un. […] Ce trait implémente la méthode foreach d’Iterable en parcourant tous les éléments à l’aide de l’iterator.

Itérateur :

Les iterators sont des structures de données permettant de parcourir une séquence d’éléments. Ils ont une méthode hasNext pour vérifier si un élément est disponible et une méthode suivante qui retourne l’élément suivant et le supprime de l’iterator.

Un iterator est mutable: la plupart des opérations changent d’état. Bien qu’il soit souvent utilisé pour parcourir les éléments d’une collection, il peut également être utilisé sans aucune sauvegarde (voir les constructeurs de l’object compagnon).

Avec un Iterator vous pouvez arrêter une itération et la poursuivre plus tard si vous le souhaitez. Si vous essayez de le faire avec un Iterable il recommencera à partir de la tête:

 scala> val iterable: Iterable[Int] = 1 to 4 iterable: Iterable[Int] = Range(1, 2, 3, 4) scala> iterable.take(2) res8: Iterable[Int] = Range(1, 2) scala> iterable.take(2) res9: Iterable[Int] = Range(1, 2) scala> val iterator = iterable.iterator iterator: Iterator[Int] = non-empty iterator scala> if (iterator.hasNext) iterator.next res23: AnyVal = 1 scala> if (iterator.hasNext) iterator.next res24: AnyVal = 2 scala> if (iterator.hasNext) iterator.next res25: AnyVal = 3 scala> if (iterator.hasNext) iterator.next res26: AnyVal = 4 scala> if (iterator.hasNext) iterator.next res27: AnyVal = () 

Notez que je n’ai pas utilisé Iterator . La raison en est que c’est difficile à utiliser. hasNext et next sont les deux seules méthodes garanties pour fonctionner comme prévu sur Iterator . Voir le Scaladoc à nouveau:

Il est particulièrement important de noter que, sauf indication contraire, il ne faut jamais utiliser un iterator après avoir appelé une méthode dessus. Les deux exceptions les plus importantes sont également les seules méthodes abstraites: next et hasNext.

Ces deux méthodes peuvent être appelées plusieurs fois sans avoir à supprimer l’iterator. Notez que même hasNext peut provoquer une mutation – par exemple lors d’une itération à partir d’un stream d’entrée, où il sera bloqué jusqu’à ce que le stream soit fermé ou qu’une entrée soit disponible.

Considérez cet exemple pour une utilisation sûre et dangereuse:

 def f[A](it: Iterator[A]) = { if (it.hasNext) { // Safe to reuse "it" after "hasNext" it.next // Safe to reuse "it" after "next" val remainder = it.drop(2) // it is *not* safe to use "it" again after this line! remainder.take(2) // it is *not* safe to use "remainder" after this line! } else it } 

Une autre explication de Martin Odersky et Lex Spoon:

Il y a une différence importante entre la méthode foreach sur les iterators et la même méthode sur les collections traversables: lorsqu’elle est appelée sur un iterator, foreach laissera l’iterator à la fin de son exécution. Donc, appeler à nouveau sur le même iterator échouera avec une exception NoSuchElementException. En revanche, foreach, lorsqu’il est appelé sur une collection, laisse le nombre d’éléments de la collection inchangé (sauf si la fonction transmise ajoute des éléments à la suppression, mais cela est déconseillé, car cela peut donner des résultats surprenants).

Source: http://www.scala-lang.org/docu/files/collections-api/collections_43.html

Notez aussi (grâce à Wei-Ching Lin pour cette astuce) Iterator étend le trait TraversableOnce alors Iterable ne le fait pas.