scala - ¿Cuál es la relación entre Iterable e Iterator?
(2)
¿Cuál es la diferencia entre Iterator
e Iterable
en scala?
Pensé que Iterable
representa un conjunto que puedo repetir, y Iterator
es un "puntero" para uno de los elementos en el conjunto iterable.
Sin embargo, Iterator
tiene funciones como para cada forEach
, map
, foldLeft
. Se puede convertir a Iterable
través de Iterable
. Y, por ejemplo, scala.io.Source.getLines
devuelve scala.io.Source.getLines
, no Iterable
.
Pero no puedo hacer groupBy
en groupBy
y puedo hacerlo en Iterable
.
Entonces, ¿cuál es la relación entre esos dos, Iterator
e Iterable
?
En resumen: un Iterator
tiene estado, mientras que un Iterator
no.
Ver los documentos API para ambos.
Iterable :
Un rasgo base para colecciones iterables.
Este es un rasgo básico para todas las colecciones de Scala que definen un método iterador para recorrer paso por paso los elementos de la colección. [...] Este rasgo implementa el método foreach de Iterable al recorrer todos los elementos usando el iterador.
Iterator :
Iteradores son estructuras de datos que permiten iterar sobre una secuencia de elementos. Tienen un método hasNext para verificar si hay un próximo elemento disponible, y un siguiente método que devuelve el siguiente elemento y lo descarta del iterador.
Un iterador es mutable: la mayoría de las operaciones en él cambian su estado. Si bien a menudo se utiliza para recorrer los elementos de una colección, también se puede usar sin tener el respaldo de ninguna colección (ver constructores en el objeto complementario).
Con un Iterator
puede detener una iteración y continuarla más tarde si lo desea. Si intenta hacer esto con un Iterable
, comenzará desde la cabeza otra vez:
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 = ()
Tenga en cuenta que no Iterator
take
on Iterator
. La razón de esto es que es difícil de usar. hasNext
y el next
son los únicos dos métodos que están garantizados para funcionar como se espera en Iterator
. Ver el Iterator nuevo:
Es de particular importancia señalar que, a menos que se indique lo contrario, nunca se debe usar un iterador después de llamar a un método en él. Las dos excepciones más importantes son también los únicos métodos abstractos: next y hasNext.
Ambos métodos se pueden llamar cualquier cantidad de veces sin tener que descartar el iterador. Tenga en cuenta que incluso hasNext puede causar mutación, como al iterar desde una secuencia de entrada, donde se bloqueará hasta que se cierre la secuencia o cuando haya alguna entrada disponible.
Considere este ejemplo para un uso seguro e inseguro:
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 }
Otra explicación de Martin Odersky y Lex Spoon:
Hay una diferencia importante entre el método foreach en los iteradores y el mismo método en las colecciones rastreables: cuando se llama a un iterador, foreach dejará el iterador al final cuando termine. Así que volver a llamar nuevamente en el mismo iterador fallará con una NoSuchElementException. Por el contrario, cuando se invoca en una colección, foreach deja la cantidad de elementos en la colección sin cambios (a menos que la función pasada se agregue para eliminar elementos, pero esto se desaconseja, ya que puede llevar a resultados sorprendentes).
Fuente: http://www.scala-lang.org/docu/files/collections-api/collections_43.html
Tenga en cuenta también (gracias a Wei-Ching Lin por este consejo) Iterator
amplía el rasgo TraversableOnce
mientras que Iterable
no lo hace.