Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the relation between Iterable and Iterator?

Tags:

iterator

scala

What is the difference between Iterator and Iterable in scala?

I thought that Iterable represents a set that I can iterate through, and Iterator is a "pointer" to one of the items in the iterable set.

However, Iterator has functions like forEach, map, foldLeft. It can be converted to Iterable via toIterable. And, for example, scala.io.Source.getLines returns Iterator, not Iterable.

But I cannot do groupBy on Iterator and I can do it on Iterable.

So, what's the relation between those two, Iterator and Iterable?

like image 751
Karel Bílek Avatar asked Jul 02 '12 22:07

Karel Bílek


People also ask

What is the difference between iterator and Iterable and how do you use them?

Iterable is an object, that one can iterate over. It generates an Iterator when passed to iter() method. An iterator is an object, which is used to iterate over an iterable object using the __next__() method. Iterators have the __next__() method, which returns the next item of the object.

What is difference between Iterable and iterator in Java?

Iterator is an interface, which has implementation for iterate over elements. Iterable is an interface which provides Iterator.

Is an iterator always Iterable?

Iterators are also Iterables. We can get an iterator from an iterable by calling iter() function. Similarly, we can call iter() function on the iterator itself. Then, it will return the iterator object itself.

Does iterator extends Iterable?

Both Iterator and Iterable are interfaces in Java that look very similar and are often confusing for beginners, but both are two different things. In short, if any class implements the Iterable interface, it gains the ability to iterate over an object of that class using an Iterator.


1 Answers

In short: An Iterator does have state, whereas an Iterable does not.

See the API docs for both.

Iterable:

A base trait for iterable collections.

This is a base trait for all Scala collections that define an iterator method to step through one-by-one the collection's elements. [...] This trait implements Iterable's foreach method by stepping through all elements using iterator.

Iterator:

Iterators are data structures that allow to iterate over a sequence of elements. They have a hasNext method for checking if there is a next element available, and a next method which returns the next element and discards it from the iterator.

An iterator is mutable: most operations on it change its state. While it is often used to iterate through the elements of a collection, it can also be used without being backed by any collection (see constructors on the companion object).

With an Iterator you can stop an iteration and continue it later if you want. If you try to do this with an Iterable it will begin from the head again:

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

Note, that I didn't use take on Iterator. The reason for this is that it is tricky to use. hasNext and next are the only two methods that are guaranteed to work as expected on Iterator. See the Scaladoc again:

It is of particular importance to note that, unless stated otherwise, one should never use an iterator after calling a method on it. The two most important exceptions are also the sole abstract methods: next and hasNext.

Both these methods can be called any number of times without having to discard the iterator. Note that even hasNext may cause mutation -- such as when iterating from an input stream, where it will block until the stream is closed or some input becomes available.

Consider this example for safe and unsafe use:

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 } 
like image 124
kiritsuku Avatar answered Oct 01 '22 03:10

kiritsuku