3 min read

Iterators in Java

Iterators are a way to traverse over the elements in a collection.
Iterators in Java

What is an Iterator?

An iterator is an object that allows you to traverse over a collection of elements. An iterator contains a cursor position. The cursor position is similar to a counter in a loop for the current index, except you don't have access to the counter.

Iterators were used more in the early days of Java. With modern Java, iterators are not obsolete, but their usage is not very common. It is still something that you should know as a Java developer, and there are situations where an iterator is still a solution that makes sense.

The Iterable Interface

The Iterable interface was introduced in Java 5 along with enhanced for statements, more commonly referred to as for-each loops. The Iterable interface is an interface implemented on all Java collections. It contains a factory method to provide an implementation of an iterator. There is one method on this interface, excluding the default methods.

public interface Iterable<T> {
    Iterator<T> iterator();
}

Any class that implements this interface can be used with for-each loops. An example of a for-each loop is the following:

for (final Integer element : numbers) {
    // ...
}

The Iterator Interface

The Iterator interface has four methods.

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
    void forEachRemaining(Consumer<? super E> action);
}

Before you can get the next element or remove it, you must first check to see if it has a next element using the hasNext() method.

iterator.hasNext();

After you have checked to see if there are anymore elements, you can get the next element using the next() method.

final var nextElement = iterator.next();

You can remove the current element using the remove() method.

iterator.remove();

When using an iterator, you can basically turn it into a for-each loop at anytime using the forEachRemaining() method. This isn't commonly used but can be useful.

iterator.forEachRemaining(
    element -> System.out.println(element);
)

A full example of using an iterator is shown in the following example:

public void removeEvenNumbers(final List<Integer> numbers) {
    final Iterator<Integer> iterator = numbers.iterator();

    while (iterator.hasNext()) {
        final Integer number = iterator.next();

        if (number % 2 == 0) {
            iterator.remove();
        }
    }
}

This method first creates an iterator. In the while loop, the condition is if there are additional elements. In each iteration of the while loop, first the next element is obtained. If the number is even, then it is removed from the collection.

Looping and Removing Elements

There is a scenario where looping through a collection and trying to remove an element from it will throw an exception.

final var numbers = new HashSet<Integer>();
set.add(1);
set.add(2);
set.add(3);

for (final Integer number : numbers) {
    if (number % 2 == 0) {
        numbers.remove(number);
    }
}

This same example using a forEach() method instead will also throw an exception. This for-each loop is basically the following:

for (final Iterator<Number> i = numbers.iterator(); i.hasNext();) {
    // ...
}

If the size of the collection changes while you are using this iterator, it will throw a ConcurrentModificationException. This is because the for-each loop iterator contains the original size of the collection. If the size changes, this can change the order of the elements in the collection, so this exception will be thrown.

This code is better written using an iterator with the following:

final Iterator<Integer> iterator = numbers.iterator();

while (iterator.hasNext()) {
    final Integer next = iterator.next();
    
    if (next % 2 == 0) {
        iterator.remove(next);
    }
}

Another way to do this is with a Stream.

final Set<Number> oddNumbers = numbers.stream()
        .filter(number -> number % 2 == 0)
        .collect(Collectors.toSet());

The difference with this example is it will produce a new collection instead of modifying the original.

Conclusion

Iterators are a way to iterate over the elements in different collections. With modern Java, it is usually better to use a for-each loop, a forEach() method, or a Stream instead of an Iterator.