Java 8 forEach

Java 8 forEach

4 Comments

One common requirement in Java application is to iterate through the elements of a collection. Prior to Java 8, the three most common ways to iterate through a collection are by using the while loop, for loop, and enhanced for loop. As the Java Collection interface extends Iterable, you can also use the hasNext() and next() methods of Iterable to iterate through collection elements.

Starting from Java 8, we have a new forEach method in Iterable to loop through elements in a collection – but in a different way.

In this post, I will discuss the forEach method introduced in Java 8.

External vs Internal Iterators

Based on who controls iterations, Java Iterators can be classified into external and internal iterators.

External iterators are also known as active or explicit iterators. When using an external iterator, the client code performing the iteration controls the iteration. The client creates the iterator and instructs it when to advance to the next element. The client also checks whether or not all the element has been visited, and so on.

Enumerations, iterators, and enhanced for-loop are all examples of external iterators. Remember the old hasMoreElements() and nextElement() of Enumeration that you write to control iteration? Similarly, next() and hasNext() of Iterator?

The enhanced for loop introduced in Java 5 is another example of external iterator. An example of the enhanced for loop is this.

for (String name : names) {
    System.out.println(name);
}

As evident from this code, the enhanced for loop shields developers from explicitly controlling looping via code. However, internally the next() and hasNext() methods get called, and therefor making it an external iterator.

Internal iterators are also known as passive, implicit or callback iterator. When you use an internal iterator, it is the iterator itself that controls the iteration. The client code essentially says to the iterator, “perform this operation on the elements in the collection.

Internal iterator has been introduced in Java 8 with the introduction of Lambda expression. The forEach method of this post covers is an internal iterator.

List Iteration using Java 8 forEach

The code to iterate through the elements of a list using forEach is this.

public static void iterateThroughList(List<String> list){
    list.forEach(name->System.out.println(name));
}

This code declaratively states what is meant to be done with the elements of the List. The internal iterator manages the iterations in the background.

To make the code clearer, the same iterateThroughList() method can be written using method reference like this.

public static void iterateThroughList(List<String> list){
        list.forEach(System.out::println);
}

You can test the code with this.

List<String> cityList = Arrays.asList("New York City", "Chicago", "Washington DC", "Denver" );
ListIterationDemo.iterateThroughList(cityList);

The output of the code in the IntelliJ console is this.
forEach List Iteration

Inside Java 8 forEach

In the example we just saw, we used lambda expressions. Let us dissect the forEach method and look what is happening inside.

The signature of the forEach method is this.

default void forEach(Consumer<? super T> action)

The forEach method performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception. The Consumer parameter of forEach is a functional interface with the accept(Object) method.

We can therefore rewrite the previous iterateThroughList() method like this.

public static void iterateThroughList(List<String> list){
    Consumer<String> consumerNames = new Consumer<String>() {
        public void accept(String name) {
            System.out.println(name);
        }
    };
    list.forEach(consumerNames);
}

This code uses an anonymous class to instantiate a Consumer implementation. The Consumer instance is then passed as an argument to forEach. This code will produce the same result as the lambda expression we wrote.

list.forEach(name->System.out.println(name));

Map Iteration using Java 8 forEach

Map in Java does not extends Iterable and therefore does not inherit Iterable’s forEach. However, Map itself has its own forEach method that you can use to iterate through key-value pairs.

The following code uses a lambda expression to do so.

public static void iterateThroughMap(Map<?,?> map){
    map.forEach((k,v) -> {System.out.println("Key: "+k+" Value: "+v);});
}

You can test the code, with this.

Map<String,String> countryCapitalMap = new HashMap<>();
countryCapitalMap.put("US", "Wshington DC");
countryCapitalMap.put("England", "London");
countryCapitalMap.put("France", "Paris");
IterationDemo.iterateThroughMap(countryCapitalMap);

The output of the code in the IntelliJ console is this.
forEach Map Iteration

Stream Iteration using Java 8 forEach

With both the new forEach method and the Java 8 Stream API, you can create a stream of elements in a collection and then pipeline the stream to a forEach method for iteration.

The code to iterate through a stream of elements in a List is this.

public static void iterateThroughListStream(List<String> list){
    list.stream().forEach(System.out::println);
}

The code to test this method is this.

List<String> countryList = Arrays.asList("Argentina", "Brasil", "China", "United States");
IterationDemo.iterateThroughListStream(countryList);

The output of the code is this.

For parallel streams, the only difference is that you need to call the parallelStream() method instead of stream() on the List. Then iterate through the stream of elements using forEach, like this.

public static void iterateThroughListParallelStream(List<String> list){
    list.parallelStream().forEach(System.out::println);
}

The code to test this method is this.

List<String> countryList = Arrays.asList("Argentina", "Brasil", "China", "United States");
IterationDemo.iterateThroughListParallelStream(countryList);

The output of the code is this.
forEach Parallel Stream Iteration

As you can notice, the order in which the list elements are processed is not the order in which the elements are stored in the list. However, when dealing with larger sets of data, parallel streams bring in considerable performance gain to your program.

Conclusion

The new forEach method of Java 8 brings in a more declarative approach to iteration. As a developer, you write code to specify the result rather than how to compute it.

This new declarative approach is more readable but also less error prone.

In addition, the forEach() method fits intuitively with the Stream API and particularly makes using parallel streams easy.

About jt

    You May Also Like

    4 comments on “Java 8 forEach

    1. January 25, 2018 at 1:56 pm

      Nice job! I’m a reviewer for an upcoming book and this aligns very well with what is there in more detail. It’s very practical and your readers should be able to take this and refactor legacy code. The key is of course unit testing this and seeing that passing lambda creates an interesting twist to the process. (In case you were looking to build on this.)

      Reply
    2. August 27, 2018 at 9:42 pm

      Nice article.
      In the first paragraph it is written, “As the Java Collection interface extends Iterable, you can also use the hasNext() and next() methods of Iterable to iterate through collection elements.”
      I think that should be corrected as hasNext() and next() methods are not of ‘Iterable’ but of ‘Iterator’, which can be fetched using iterator() method of Iterable and then hasNext() and next() methods of Iterator can be used to iterate through collection elements.

      Reply
    3. January 24, 2020 at 11:19 am

      Great article JT, yeeep forEach is now my favorite method to iterate over collections and streams in Java.

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.