4. Using forEach method with Lambda Expressions
Before going to each kind of collection iteration, suppose that we have a List collection initialized as follows:List<String> listNames = new ArrayList<>(); listNames.add("Tom"); listNames.add("Mary"); listNames.add("Peter"); listNames.add("John"); listNames.add("Kim");This list contains names of all students in a class. Note that the diamond operator <>used in the right side of the assignment:
ArrayList<>();This syntax can be used from Java 7, which allows us to declare generics collections in a more compact way, as the compiler can infer the parameter type in the right side from the left side (thus the so-called type inference).Now, let’s look at each method in details.
for (int i = 0; i < listNames.size(); i++) { String aName = listNames.get(i); System.out.println(aName); }Output:
Tom Mary Peter John KimHere are the pros of this method:
Iterator<String> iterator = listNames.iterator(); while (iterator.hasNext()) { String aName = iterator.next(); System.out.println(aName); }This code snippet does the same thing as the classic for loop example above. You may need some explanations:
Set<Integer> numbers = new HashSet<>(); numbers.add(100); numbers.add(35); numbers.add(89); numbers.add(71); Iterator<Integer> iterator = numbers.iterator(); while (iterator.hasNext()) { Integer aNumber = iterator.next(); System.out.println(aNumber); }Output:
35 100 71 89And here’s another example demonstrating how to iterate over a Map using an interator:
Map<Integer, String> mapAscii = new HashMap<>(); mapAscii.put(65, "A"); mapAscii.put(66, "B"); mapAscii.put(67, "C"); mapAscii.put(68, "D"); Iterator<Integer> keyIterator = mapAscii.keySet().iterator(); while (keyIterator.hasNext()) { Integer key = keyIterator.next(); String value = mapAscii.get(key); System.out.println(key + " -> " + value); }Output:
65 -> A 66 -> B 67 -> C 68 -> DBecause the map stores elements in form of key=value pairs, first we need to get the iterator of the keys (a Set collection), then use this iterator to get each key, and retrieve the value corresponds to that key.
for (String aName : listNames) { System.out.println(aName); }Does it look nicer, doesn’t it? The code is more compact and more readable. That’s why this construct is called enhanced for loop - an enhanced feature of the Java programming language. NOTES: The enhanced for loop actually uses an iterator behind the scenes. That means the Java compiler will convert the enhanced for loop syntax to iterator construct when compiling. The new syntax just gives the programmers a more convenient way for iterating over collections.Using the enhanced for loop, we can re-write the code to iterate the Set collection above like this:
for (Integer aNumber : numbers) { System.out.println(aNumber); }Compare to the previous code (using iterator), this code is incredible simpler and more understandable right?And the code that iterates over a Map can be re-written using the enhanced for loop like this:
for (Integer key : mapAscii.keySet()) { String value = mapAscii.get(key); System.out.println(key + " -> " + value); }Wow! This looks much simpler than the previous code using iterator, right? Thanks to the enhanced for loop - it helps programmers write code more quickly and more readable.As the Java programming language evolves, we have a new method which is describe below.
listNames.forEach(name -> System.out.println(name));Amazing! This code looks even more compact and more readable than the enhanced for loop version. As we can read the above line like this: for each element in the listNames, print the name to the console.Since Java 8, each collection has a forEach() method that implements the iteration internally. Note that this method takes a Lambda expression or in other words, the programmers can pass their code - or function - into this method. As shown in the above example, the code to print each element is passed into the method.If you are new to Lambda expressions, check this tutorial:
Lambda Expressions - The Java Tutorials
To help you understand more. Suppose that we have some code to deal with each student name like this:class StudentHelper { public static void process(String name) { System.out.println("processing the student " + name); } }Then we can pass this code into the forEach() method like this:
listNames.forEach(StudentHelper::process);As you can see, this is the power of using Lambda expressions with the internal iteration method. The code is highly compact, more readable and more flexible.Similarly, we can use the forEach method to iterate over the Set above like this:
numbers.forEach(number -> System.out.println(number));Or even more compact:
numbers.forEach(System.out::println);And code to iterate over the Map above using the forEach method:
mapAscii.forEach((key, value) -> System.out.println(key + " -> " + value));WOW! Incredible simple, right?
listNames.stream().map(e -> e.toUpperCase()).forEach(System.out::println);To learn more about Java Stream API, check this tutorial. So far I have walked you through the five different ways for iterating over collections in the Java programming language: classic for loop, iterator, enhanced for loop, for each and Java Stream API. The later is always better than the former, that’s how the language evolves over time. Using which one is depending on your need and personal style.Nowadays, the forEach method has been being adopted and preferred as it is deadly simple, convenient and flexible.