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.

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 the Iterable until all elements have been processed or an exception occurred.
  • default Spliterator<T> spliterator(): creates a Spliterator over the elements
  • Iterator<T> iterator(): returns an Iterator over elements of type T.

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 type E in the iteration.
  • boolean hasNext(): returns true if the iteration still has one or more elements.
  • void remove(): removes the element from the underlying collection which is the last element returned from Iterator.

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 an Iterator to get the iteration state.
  • The Iterable itself has a method returns an Iterator.
  • The Collection interface extends Iterable, that’s why you can directly use the Iterable‘s methods on any subclass belong to this Collection interface, whose elements are intrinsically iterable.
  • Classes implementing Iterable must override iterator() method, while it’s obliged to override next() and hasNext() methods if a class implements Iterator.
  • You cannot modify elements with an Iterable, but an Iterator can give you the current state of the iteration with the next() and hasNext() methods, and remove an element from the underlying collection with the remove() method.
Previous Article
Next Article

Subscribe to my newsletter to get weekly updates

Categories

Archives