Java 8 forEach
4 CommentsLast Updated on May 20, 2019 by Simanta
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.
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.
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.
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.
Brent Stains
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.)
GM
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.
Azhwani
Great article JT, yeeep forEach is now my favorite method to iterate over collections and streams in Java.