Friday, August 6, 2021

Java 8 - Journey of for loop in Java - for() to forEach() Examples

for loop has come a long way in Java 8 with a new forEach() method in java.util.stream.Stream class. In this article, we will take a look at the journey of for loop in different versions of the Java programming language. for loop is there from the very beginning of Java i.e. JDK 1.0. Almost all Java programmers have used the classical for() loop, as it is also an essential programming construct, I guess just next to if-else, but when the foreach loop or as some people call it enhanced for loop was introduced in JDK 1.5, everything changed from looping over Collection and array perspective.

Yesterday's popular looping construct becomes old and ugly and a more elegant solution took over. It was shorter, cleaner, and less error-prone, what else did you need at that time.

Things were quiet in terms of for loop from the last 10 years, but  In Java 8, looping has taken another big step in its new avatar, forEach() method. It's not only short, clean but also takes advantage of lazy evaluation and inbuilt parallelism of Java 8 Stream. let's revisit this journey of for loop in Java in this post of Javarevisited.

Pre-JDK 1.5, this was my favorite way of iterating over an ArrayList or HashSet :

// pre JDK 1.5, printing each element of List using for loop
List<String> countries = Arrays.asList("India", "Australia", 
         "England", "South Africa");
        
for(int i=0; i < countries.size(); i++){
   System.out.println(countries.get(i));
}

So when foreach loop or advanced for loop was added on Java 5, I quickly adapted to the following style of looping over Collection or List :

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

This was much shorter, clearer, and less error-prone than the previous one and everybody loved it. You don't need to keep track of the index, you don't need to call size() method in every step and it was less error-prone than the previous one, but it was still imperative. You are telling the compiler what to do and how to do like traditional for loop.




Things change drastically when the functional style of programming was introduced in Java 8 via lambda expression and new Stream API. Now you can loop over your collection without any loop in Java, you just need to use forEach() method of java.util.Stream class, as shown below :

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

This code has reduced looping over collection or list into just one line. To add into, If you want to perform some pre-processing task e.g. filtering, conversion or mapping, you can also do this in the same line as shown below :

countries.stream()
         .filter(country -> country.contains("n"))
         .forEach(str -> System.out.println(str));

This code will now only print "India" and "England" as it contains the letter "n". Java SE 8 for Really Impatient By Cay S. Horstmann  to learn more about filtering in Java 8.



This approach has several advantages over the previous two imperative approaches, first, it segregates what to do from how to do it. You are just telling API to filter the list based upon the condition you have given and then print those elements. Now API can print it by the way it wants, you don't need to worry about it.

The benefit is, you don't need to write imperative code, you will also benefit from lazy evaluation and any performance improvement done by API to accomplish your task. You can even make your code more concise using the method reference feature of Java 8, as shown below :

countries.stream()
         .filter(country -> country.contains("n"))
         .forEach(System.out::println);

That "::" or scope resolution operator of C++ is used for method reference since you are not doing anything with String argument of println() then just passing it, you can use method reference there to make your code more clean and concise.

for loop changes in Java 1.4, 5 and 8


Here is the complete code example of using different for loops in Java world, including Java 8 forEach() method. This example demonstrates gradual changes made in for loop from Java 1.4, 5, and Java 8 versions.



package test;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;


/**
 * Java Program to demonstrate different ways to loop over collection in 
 * pre Java 8 and Java 8 world using Stream's forEach method.
 * @author Javin Paul
 */
public class JourneyOfForLoopInJava {

    public static void main(String args[]) throws IOException {

        // pre JDK 1.5, printing each element of List using for loop
        List<String> countries = Arrays.asList("India", 
                     "Australia", "England", "South Africa");
        
        for(int i=0; i<countries.size(); i++){
            System.out.println(countries.get(i));
        }
        
        
        // In Java 5, we god advanced for loop, 
        // which makes it easy to loop over
        // List or Collection        
        for(String country : countries){
            System.out.println(country);
        }
        
        // In Java 8, you can use forEach method of Stream class to loop over
        // any List or Collection
        countries.stream().forEach(str -> System.out.println(str));
        
        // doing some pre-processing filtering on our list
    // will print India, England as only they contain "n"
        countries.stream()
                .filter(country -> country.contains("n"))
                .forEach(str -> System.out.println(str));
        
        // making the code more concise using method reference
         countries.stream()
                .filter(country -> country.contains("n"))
                .forEach(System.out::println);
    }


}

So you have seen, for loop has come a long way from JDK 1 to JDK 8, especially when you use with Collection. Gone are the days, when you need to worry about looping index, initializing it properly, making sure it ends properly to avoid IndexOutOfBoundsException, and calling the corresponding to get() method on List.

Java 5 was the first step in making looping over Collection easy but Java 8 has provided a much powerful functional style of looping in Java. In fact, it's not even a loop it's just a method call, which means you can write high-performance Java Collection code without using a loop.

So next time, you need to loop over the collection, just don't use a simple or advanced for loop, considering using the forEach() method of Stream. You will not only benefit from the modern way of filtering and mapping but also from lazy evaluation and performance improvement by smart looping implementation of API itself.


Related Java 8 Tutorials
If you are interested in learning more about the new features of Java 8, here are my earlier articles covering some of the important concepts of Java 8:
  • 20 Examples of Date and Time in Java 8 (tutorial)
  • How to use Stream class in Java 8 (tutorial)
  • How to use filter() method in Java 8 (tutorial)
  • How to use forEach() method in Java 8 (example)
  • How to join String in Java 8 (example)
  • How to convert List to Map in Java 8 (solution)
  • How to use peek() method in Java 8 (example)
  • 5 Books to Learn Java 8 from Scratch (books)
  • How to convert the stream to array in Java 8 (tutorial)
  • Java 8 Certification FAQ (guide)
  • Java 8 Mock Exams and Practice Test (test)

Thanks for reading this article so far. If you like this article then please share it with your friends and colleagues. If you have any questions, doubts, or feedback then please drop a comment and I'll try to answer your question.

P.S.: Java 8 is a bundle of many exciting features like this, if you are interested to learn more about them, you can pick any of the these recommend Java 8 books like Java 8 in Action: Lambdas, Streams, and functional-style programming or Mastering Lambdas: Java Programming in a Multicore World.

9 comments :

Anonymous said...

I thought generics were introduced with JDK 1.5. Wouldn't that made the "pre JDK 1.5" example wrong ?

javin paul said...

@Anonymous, well yes, generics was introduced in JDK 1.4 but the code was to demonstrate style of looping :)

Vladimir Dolzhenko said...

The most weird (and the worst) way to iterate over list with size and get - there are several pitfalls like if list is LinkedList - in this case complexity is O(n^2) instead of O(1) - or CopyOnWriteArrayList - one thread changing list while another thread iterates over it using size() / get() leads to that IndexOutOfBoundsException.

So - you made problems by your own using the bad practises as favorite.

The right approach (pre 1.4 and now days) is using Iterator:

for(final Iterator it = list.iterator(); it.hashNext(); ){
Object o = it.next();
}

it is the best and the safetiest way to iterate over any collection w/o unpredictable bahaviour - moreover foreach loop is just syntax sugar over such construction.

javin paul said...

@Vladimir, Indeed Iterator is better way to iterate over List in Java 1.4, it not only hide the implementation detail like you said LinkedList doesn't support index based random search and could drastically affect performance, it provides a consistent way to loop through.

Vladimir Dolzhenko said...

@Javin - iterator is the only correct way available in java - java 1.1, 1.2, 1.3... and 1.7

As I said foreach loop is just a syntax sugar over iterator.

There is an alternative way to do the same in java 1.8 with stream api - BUT - that is completely another approach - the FP way - it does NOT mean iterator is not valid anymore - that's just second correct way.

YourDailyDude said...

How does foreach helps in lazy initialization and performance improvement?

javin paul said...

@nagaraju, forEach is stream based, which means implementation is free to evaluate result lazily, can decide to run the operation in parallel (e.g. printing, filtering etc) as well. That's why when you use forEach(), the order is not guaranteed.

Anonymous said...

@Javin I have a List with data like this
[{S_ID=142, BANK=2, NM_BANK=BANK XXX},{S_ID=102, BANK=2, NM_BANK=BANK XXX},{S_ID=122, BANK=2, NM_BANK=BANK XXX},{S_ID=142, BANK=3, NM_BANK=BANK XXY}]
I need move some data where S_ID=142 to a new list and remove it from current list. How can I do it in java 8?
Thanks

Achint Mittal said...

We can even directly use forEach() method from Iterable interface.

So, instead of using
countries.stream().forEach(System.out :: println);

we can write:

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

But ofcourse in this approach, we are loosing lazy evaluation feature of streams.

Post a Comment