1. Introduction

Iterating over the elements of a list is one of the most common tasks in a program.

In this tutorial, we’ll review the different ways to do this in Java. We’ll focus on iterating through the list in order, though going in reverse is simple, too.

2. for Loop

First, let’s review some for loop options.

We’ll begin by defining a list of countries for our examples:

List<String> countries = Arrays.asList("Germany", "Panama", "Australia");

2.1. Basic for Loop

The most common flow control statement for iteration is the basic for loop.

The for loop defines three types of statements separated with semicolons. The first statement is the initialization statement. The second one defines the termination condition. The last statement is the update clause.

Here we’re simply using an integer variable as an index:

for (int i = 0; i < countries.size(); i++) {
    System.out.println(countries.get(i));
}

In the initialization, we must declare an integer variable to specify the starting point. This variable typically acts as the list index.

The termination condition is an expression that returns a boolean after evaluation. Once this expression evaluates to false, the loop finishes.

The update clause is used to modify the current state of the index variable, increasing it or decreasing it until the point of termination.

2.2. Enhanced for Loop

The enhanced for loop is a simple structure that allows us to visit every element of a list. It’s similar to the basic for loop, but more readable and compact. Consequently, it’s one of the most commonly used forms to traverse a list.

Notice that the enhanced for loop is simpler than the basic for loop:

for (String country : countries) {
    System.out.println(country); 
}

3. Iterators

An Iterator is a design pattern that offers us a standard interface to traverse a data structure without having to worry about the internal representation.

This way of traversing data structures offers many advantages, among which we can emphasize that our code doesn’t depend on the implementation.

Therefore, the structure can be a binary tree or a doubly linked list, since the Iterator abstracts us from the way of performing the traversal. In this way, we can easily replace data structures in our code without unpleasant problems.

3.1. Iterator

In Java, the Iterator pattern is reflected in the java.util.Iterator class. It’s widely used in Java Collections. There are two key methods in an Iterator, the hasNext() and next() methods.

Here we’ll demonstrate the use of both:

Iterator<String> countriesIterator = countries.iterator();

while(countriesIterator.hasNext()) {
    System.out.println(countriesIterator.next()); 
}

The hasNext() method checks if there are any elements remaining in the list.

The next() method returns the next element in the iteration.

3.2. ListIterator

A ListIterator allows us to traverse a list of elements in either forward or backward order.

Scrolling a list with ListIterator forward follows a mechanism similar to that used by the Iterator. In this way, we can move the iterator forward with the next() method, and we can find the end of the list using the hasNext() method.

As we can see, the ListIterator looks very similar to the Iterator that we used previously:

ListIterator<String> listIterator = countries.listIterator();

while(listIterator.hasNext()) {
    System.out.println(listIterator.next());
}

4. forEach()

4.1. Iterable.forEach()

Since Java 8, we can use the forEach() method to iterate over the elements of a list.  This method is defined in the Iterable interface, and can accept Lambda expressions as a parameter.

The syntax is pretty simple:

countries.forEach(System.out::println);

Before the forEach function, all iterators in Java were active, meaning they involved a for or while loop that traversed the data collection until a certain condition was met.

With the introduction of forEach as a function in the Iterable interface, all classes that implement Iterable have the forEach function added.

4.2. Stream.forEach()

We can also convert a collection of values to a Stream, and have access to operations such as forEach()map(), and filter().

Here we’ll demonstrate a typical use for streams:

countries.stream().forEach((c) -> System.out.println(c));

5. Conclusion

In this article, we demonstrated the different ways to iterate over the elements of a list using the Java API. These options included the for loop, enhanced for loop, Iterator, ListIterator, and the forEach() method (included in Java 8).

Then we learned how to use the forEach() method with Streams.

Finally, all the code used in this article is available in our Github repo.