Iterable and Iterator in Java4 min read
A lot of people feel confused about the Iterator
and Iterable
interfaces in Java, because they have very similar names and sometimes they work together which makes people hardly tell them apart. In this article, we are going to elucidate the difference between them and see how they can be used through some examples.
In short, the Iterable
interface belongs to the java.lang
package, while the Iterator
interface is a part of java.util
package. An Iterable
is a simple representation of a series of elements that can be iterated over, but it doesn’t provide any iteration state to get the “current element”. An Iterator
, on the other hand, allows you to iterate over elements of iterable objects. You can iterate through each element of the collection by using the hasNext()
and next()
methods of this interface.
Table of Contents
Iterable and Iterator Interfaces
For the Iterable
interface, there are some common methods:
default void forEach(Consumer<? super T> action)
: perform the given function on each element of theIterable
until all elements have been processed or an exception occurred.default Spliterator<T> spliterator()
: creates aSpliterator
over the elementsIterator<T> iterator()
: returns anIterator
over elements of typeT
.
Any class implements this interface needs to override the iterator()
method.
Here are some frequently used methods of the Iterator
interface:
E next()
: returns the next element with typeE
in the iteration.boolean hasNext()
: returnstrue
if the iteration still has one or more elements.void remove()
: removes the element from the underlying collection which is the last element returned fromIterator
.
Any class implements the Iterator
interface need to override the next()
and hasNext()
method.
As you can see, the relationship between the Iterable
and Iterator
interface is close, the implementation of Iterable
is one that provides Iterator
of itself.
A Typical Example
Every class in the Collection interface indirectly implement the Iterable
interface, that’s why we can use every method defined in the Iterable
interface, for example, we have a List
like this:
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
nums.forEach(/* some code */);
nums.iterator();
nums.spliterator();
For more specific, the ArrayList class implements the List interface, the List interface implements the Collection interface, the Collection interface extends the Iterable interface.
The crux here is here any collection implements Iterable
interface gives you the iterator()
method, which returns an Iterator
and we can use this to iterate through each element of the collection:
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Iterator<Integer> iterator = nums.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
// output: 1 2 3 4 5
}
We can also make the use of the for-each
loop, which internally calls the iterator()
method on an object:
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
for (Integer num : nums) {
System.out.print(num + " ");
}
}
The forEach()
method can also be used to iterate over a collection, which also uses for-each internally:
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
nums.forEach(e -> System.out.print(e + " ")); // 1 2 3 4 5
}
More Iterator examples
The Iterable
doesn’t maintain the current state of the iteration, but an Iterator
does. With an Iterator,
we can obtain the current element, moving forward if there are still some elements, and remove an element from the underlying collection if necessary:
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
Iterator<Integer> iterator = nums.iterator();
while(iterator.hasNext()) {
int i = iterator.next();
System.out.print(i + " ");
if (i % 2 == 0) {
iterator.remove();
}
}
System.out.println();
for(Integer i: nums) {
System.out.print(i + " ");
}
/* output:
1 2 3 4 5 6 7 8 9 10
1 3 5 7 9
*/
}
In this example, we just simply create a list with ten integer from 1 to 10. We then create an iterator from this collection, it iterates until there is no element left, we print each element on each iteration and then removes the even elements from the underlying collection with the remove()
method.
Notice that each time you call the next()
method, it will return the next element in the iteration, hence you need to pay a little attention to this:
while (iterator.hasNext()) {
System.out.println(iterator.next());
if(iterator.next() % 2 == 0) {
iterator.remove();
}
}
/* it will just print 1 3 5 7 9 */
Wrapping up
Let’s summarize what we have learned:
- The
Iterable
interface gives you a representation of a series of elements that can be iterated through, but we need anIterator
to get the iteration state. - The
Iterable
itself has a method returns anIterator
. - The
Collection
interface extendsIterable
, that’s why you can directly use theIterable
‘s methods on any subclass belong to this Collection interface, whose elements are intrinsically iterable. - Classes implementing
Iterable
must overrideiterator()
method, while it’s obliged to overridenext()
andhasNext()
methods if a class implementsIterator
. - You cannot modify elements with an Iterable, but an Iterator can give you the current state of the iteration with the
next()
andhasNext()
methods, and remove an element from the underlying collection with theremove()
method.