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

for loop has come a long way in Java 8 with 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 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 become old and ugly and more elegant solution took over. It was shorter, cleaner and less error prone, what else you need at that time.

Things were quite in terms of for loop from last 10 years, but  In Java 8, looping has taken another big step in its new avatar, forEach() method. Its not only short, clean but also take advantage of lazy evaluation and in built 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 adopted to following style of looping over Collection or List :

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

This was much shorter, cleaner and less error prone than previous one and everybody loved it. You don't need to keep track of index, you don't need to call size() method in every step and it was less error prone than previous one, but it was still imperative. You are telling 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 advantage over previous two imperative approaches, first it segregate what to do from how to do. You are just telling API to filter list based upon condition you have given and then print those element. Now API can print it by the way it wants, you don't need to worry about. 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 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 demonstrate 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 corresponding get() method on List. Java 5 was first step in making looping over Collection easy but Java 8 has provided a much powerful of functional style looping in Java. In fact, it's not even a loop its 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 collection, just don't use a simple or advanced for loop, considering using forEach() method of Stream. You will not only benefit from modern way of filtering and mapping but also from lazy evaluation and performance improvement by smart looping implementation of API itself.

Java 8 is bundle of many exciting features like this, if you are interested to learn more about them, you can pick any of the following books :
  • Java SE 8 for Really Impatient By Cay S. Horstmann (see here)
  • Java 8 in Action: Lambdas, Streams, and functional-style programming (see here)
  • Mastering Lambdas: Java Programming in a Multicore World (see here)

7 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.

nagaraju polavarapu 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.

Post a Comment