2012-07-02 7 views
35

¿Cuál es la diferencia entre Iterator y Iterable en scala?¿Cuál es la relación entre Iterable e Iterator?

Pensé que Iterable representa un conjunto que puedo repetir, y Iterator es un "puntero" a uno de los elementos en el conjunto iterable.

Sin embargo, Iterator tiene funciones como forEach, map, foldLeft. Se puede convertir a Iterable a través de toIterable. Y, por ejemplo, scala.io.Source.getLines devuelve Iterator, no Iterable.

Pero no puedo hacer groupBy en Iterator y puedo hacerlo en Iterable.

Entonces, ¿cuál es la relación entre esos dos, Iterator y Iterable?

Respuesta

50

En resumen: un Iterator tiene estado, mientras que un Iterable no.

Consulte los documentos de la 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 de iterador para recorrer uno a uno los elementos de la colección. [...] Este rasgo implementa el método foreach de Iterable al recorrer a través de todos los elementos usando el iterador.

Iterator:

iteradores son estructuras de datos que permiten a iterar sobre una secuencia de elementos. Tienen un método hasNext para comprobar 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. Mientras que se usa a menudo para iterar a través de los elementos de una colección, también se puede usar sin estar respaldado por ninguna colección (vea 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 que comenzará a partir de la cabeza de nuevo:

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 yo no uso take en Iterator. La razón de esto es que es difícil de usar. hasNext y next son los únicos dos métodos que están garantizados para funcionar como se espera en Iterator.Véase el nuevo Scaladoc:

Es de particular importancia tener en cuenta que, a menos que se indique lo contrario, uno nunca debe utilizar un iterador después de llamar a un método en él. Las dos excepciones más importantes de 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 la mutación - , por ejemplo, al iterar desde un flujo de entrada, donde bloqueará hasta , la transmisión se cerrará o habrá alguna entrada disponible.

Considere este ejemplo para el 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 
} 
+1

gracias, con el ejemplo, tiene mucho sentido. –

+0

Odersky y Spoon escribieron una buena introducción a las clases de la colección Scala: ver http://www.scala-lang.org/docu/files/collections-api/collections.html –

+0

He probado esto en Scala 2.11.7, iterator se comporta de manera similar a iterable, es decir, cuando invocas 'take (2)' por segunda vez, todavía obtienes 'List (1, 2)'. – qed

5

Otra explicación de Martin Odersky y Lex Cuchara:

Hay una diferencia importante entre el método foreach en iteradores y la mismo método en colecciones trasladables: cuando se llama a un iterador, foreach dejará el iterador en su extremo cuando esté hecho. Entonces, volver a llamar nuevamente en el mismo iterador fallará con NoSuchElementException. Por el contrario, cuando se solicita en una colección, foreach deja el número de elementos en la colección sin cambios (a menos que la función pasada se agregue para eliminar elementos, pero esto es desaconsejado, porque puede conducir 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 extiende el rasgo TraversableOnce mientras Iterable no.

0

Tenga en cuenta también (gracias a Wei-Ching Lin por este consejo) Iterador extiende el rasgo TraversableOnce mientras Iterable no.

En scala 2.11, tanto Iterator como Itarable amplían el rasgo TraversableOnce.

Cuestiones relacionadas