DEV Community

Cover image for Iterators in Java
Yuri Mednikov
Yuri Mednikov

Posted on • Updated on • Originally published at mednikov.tech

Iterators in Java

The iterator pattern is one of approaches to access elements of a collection, alongside with streams. From a technical point of view, the iterator traverses elements in a sequential and predictable order. In Java the behaviour of iterators is defined in java.util.Iterator contract, which is a member of Java Collections Framework.

Iterators are similar to enumerators, but there are differences between these concepts too. The enumerator provides inderect and iterative access to each element of a data structure exactly once. From the other side, iterators does the same task, but the traversal order is predictable. With this abstraction a caller can work with collection elements, without a direct access to them. Also, iterators allow to delete values from a collection during the iteration.

Access elements of a collection

As it was mentioned before, the order of accessing elements is predictable, which means that the iterator traverses elements sequentialy. Therefore, the general Java contract does not allow to access the particular element (however, that is possible using Commons Collections, even this violates the idea). In order to access the next element, use next() method. It returns an element or throws NoSuchElementException, when the iterator does not contain more elements.

In order to prevent this unchecked exception, you should call the hasNext() method prior to accessing an element.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    int x = iterator.next() * 2;
    System.out.println(x);
}
Enter fullscreen mode Exit fullscreen mode

This code snippet demonstrates the usage of the iterator for sequential retrieving of elements and the printing of double value of each element.

The important thing to note here, is that once elements are consumed, the iterator can not be used. That means, that calling the iterator after traversing will lead to an exception:

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    int x = iterator.next() * 2;
    System.out.println(x);
}
int value = iterator.next();
Enter fullscreen mode Exit fullscreen mode

The execution of the above code snippet will lead to the following result:

I already mentioned Apache Commons Collections. This library contains a helper class IteratorUtils, which has a number of static utility methods to work with iterators. While some of them violate the core pattern, they can be useful. So, alongside with a sequential access, it possible to access a particular element by its index, as well there is a wrapper method to get the first element.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
Iterator<Integer> iterator = numbers.iterator();
int first = IteratorUtils.first(iterator);
Assertions.assertThat(first).isEqualTo(1);
Enter fullscreen mode Exit fullscreen mode

Consuming an element

Since Java 8 iterators permit also to specify a consumer function, which can be performed on each element. This is a shortcut of what we can do using a while block. Let consider the first example implemented with the forEachRemaining() method. Take a look on the code snippet below:

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
Iterator<Integer> iterator = numbers.iterator();
iterator.forEachRemaining(number -> System.out.printf("This is a consumer. Value: %d \n", number*2));
Enter fullscreen mode Exit fullscreen mode

This program listing produces the following result:

It is possible to perform such behavior with IteratorUtils. It has two static methods:

  • forEach() = applies the function to each element of the provided iterator.
  • forEachButLast() = executes the defined function on each but the last element in the iterator

Both methods accept two arguments: an iterator instance and a closure function. The second concept defines a block of code which is executed from inside some block, function or iteration. Technically, it is a same thing as a consumer functional interface. The example of using this helper method is below:

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);
Iterator<Integer> iterator = numbers.iterator();
IteratorUtils.forEach(iterator, number -> System.out.printf("This is a closure. Value: %d \n", number*2));
Enter fullscreen mode Exit fullscreen mode

The output of this snippet is:

Remove elements

Finally, we need to observe how to use an iterator to delete elements. The java.util.Iterator interface defines the remove() method. It deletes from the underlying collection the last element returned by the iterator. This method can be called only once per call of the next() function. Note, that this operation is optional.

List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
System.out.println("Initial list: ");
numbers.stream().forEach(System.out::println);
Iterator<Integer> iterator = numbers.iterator();
while(iterator.hasNext()) {
    iterator.next();
    iterator.remove();
}
System.out.println("Modified list: ");
numbers.stream().forEach(System.out::println);
Enter fullscreen mode Exit fullscreen mode

This code snippet executes and prints elements of the array list before and after removing elements, like it is snown on the screenshot below:

There are things to remember, when you use iterator.remove():

  • As this operation is optional, it can throw the UnsupportedOperationException, if a collection does not have an implementation of this method (that is why I used an array list and not a generic list)
  • You must call the next() method BEFORE calling the remove() method, otherwise it will lead to the IllegalStateException to be thrown

That is how you can utilize an iterator in order to access elements of a collection, to perform an action on each element or to remove elements from a collection. These are general considerations. If you want to learn how to use iterators with concrete types of collections (lists, sets, queues) and to learn more advanced iterator patterns - check my book on Java Collections Framework.

Top comments (2)

Collapse
 
amineamami profile image
amineamami

Cool article, Thanks

Collapse
 
iuriimednikov profile image
Yuri Mednikov

Thanks for your comment. You can find more articles on mednikov.tech