2012-07-02 21 views
35

Qual è la differenza tra Iterator e Iterable in scala?Qual è la relazione tra Iterable e Iterator?

Ho pensato che Iterable rappresenti un set che posso scorrere, e Iterator è un "puntatore" a uno degli elementi nel set iterabile.

Tuttavia, Iterator ha funzioni come forEach, map, foldLeft. Può essere convertito in Iterable tramite toIterable. Ad esempio, scala.io.Source.getLines restituisce Iterator, non Iterable.

Ma non posso fare groupBy su Iterator e posso farlo su Iterable.

Quindi, qual è la relazione tra questi due, Iterator e Iterable?

risposta

50

In breve: uno Iterator ha lo stato, mentre uno Iterable no.

Vedere i documenti API per entrambi.

Iterable:

Un tratto di base per le collezioni iterabili.

Questo è un tratto di base per tutte le raccolte Scala che definiscono un iteratore metodo per esaminare uno per uno gli elementi della raccolta. [...] Questa caratteristica implementa il metodo foreach di Iterable passando lo attraverso tutti gli elementi usando iteratore.

Iterator:

Iteratori sono strutture di dati che permettono di iterare su una successione di elementi . Hanno un metodo hasNext per verificare se è disponibile un elemento successivo e un metodo successivo che restituisce l'elemento successivo e lo scarta dall'iteratore.

Un iteratore è mutabile: la maggior parte delle operazioni su di esso cambiano stato. Mentre viene spesso utilizzato per scorrere gli elementi di una raccolta, è possibile utilizzare anche senza il backup di alcuna raccolta (vedere i costruttori nell'oggetto associato).

Con un Iterator è possibile interrompere un'iterazione e proseguirla in un secondo momento, se lo si desidera. Se si tenta di fare questo con un Iterable inizierà da ancora la testa:

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 =() 

nota, che non ho usato take su Iterator. La ragione di questo è che è difficile da usare. hasNext e next sono gli unici due metodi che sono garantiti per funzionare come previsto su Iterator.Vedere il nuovo Scaladoc:

E 'di particolare importanza notare che, salvo diversa indicazione, non si dovrebbe mai usare un iteratore dopo aver chiamato un metodo su di esso. Le due eccezioni più importanti di sono anche i soli metodi astratti: next e hasNext.

Entrambi questi metodi possono essere chiamati un numero qualsiasi di volte senza dover annullare l'iteratore . Notare che anche hasNext può causare la mutazione - come quando si itera da un flusso di input, dove bloccherà fino a lo stream è chiuso o qualche input diventa disponibile.

Consideriamo questo esempio per un utilizzo sicuro e non sicuri:

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

grazie, con l'esempio, ha perfettamente senso. –

+0

Odersky e Spoon hanno scritto un buon primer sulle classi della collezione Scala: vedi http://www.scala-lang.org/docu/files/collections-api/collections.html –

+0

Ho provato questo in Scala 2.11.7, iterator si comporta in modo simile a iterable, ovvero quando invochi 'take (2)' per la seconda volta, ottieni ancora 'List (1, 2)'. – qed

5

Un'altra spiegazione da Martin Odersky e Lex Spoon:

C'è una differenza importante tra il metodo foreach su iteratori e la stesso metodo su collezioni attraversabili: quando viene chiamato in un iteratore, foreach lascerà l'iteratore alla sua fine quando è terminato. Quindi chiamare di nuovo sullo stesso iteratore fallirà con una NoSuchElementException . Al contrario, quando viene richiamato su una raccolta, il valore di lascia il numero di elementi nella raccolta invariato (a meno che la funzione passata non rimuova gli elementi, ma questo è scoraggiato, perché può portare a risultati sorprendenti).

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

nota anche (grazie a Wei-Ching Lin per questo suggerimento) Iterator estende la TraversableOnce tratto mentre Iterable non lo fa.

0

nota anche (grazie a Wei-Ching Lin per questo suggerimento) Iterator estende il tratto TraversableOnce mentre Iterable no.

In scala 2.11 sia Iterator che Itarable si estende TraversableOnce trait.

Problemi correlati