Java for loop Evolution

Filed Under: Java

Iteration is one of the most basic requirement in any programming language & of all, “for” is the most widely used loop in java for iteration. We will see the evolution of java for loop iteration techniques.

Java for loop Evolution

java for loop evolution

We will work on an example for displaying names of actors from a Collection. For the sake of simplicity let’s take a List and set this up:


List<String> actors = Arrays.asList("Jack Nicholson", "Marlon Brando", "Robert De Niro", "Al Pacino", "Tom Hanks");

Let’s start looking at the for loop evolution over different java releases.

Basic for loop

Main elements of, “for” loop are initialization, termination condition, and loop increment logic. Let’s see a sample here:


for (int i = 0; i < actors.size(); i++) {
  System.out.println(actors.get(i));
} 

If we want to iterate over a collection which is not of type List, we will not have a get(int index) method which will give us the indexed value from the collection. Hence in Java 1.2 Iterators were introduced. Let’s see how we solve the same problem with Iterators:


for (Iterator<String> iterator = actors.iterator(); iterator.hasNext();) {
 System.out.println(iterator.next());
}

for-each (Enhanced for loop)

This loop was introduced in Java 5, it removes the clutter, ceremony and the opportunity for error by hiding the iterator or index variable completely. Let’s see this in action:


for (String actor : actors) {
 System.out.println(actor);
}

The above examples are of types of External Iterators, here control & termination logic resides with an external iterator. This can result in overall complexity and may be error-prone.

Internal forEach loop

In Java 8, with the introduction of Functional Interface & Lambda’s, Java architects have provided us with an Internal Iterator (forEach loop) supported by Collection framework and can be used with the collections object. This method performs a given action for each element of the collection. Let’s see this in more detail:

Method Signature: public void forEach(Consumer action)

forEach takes an implementation of Consumer (Functional Interface), which has an accept(T t) method to execute the logic inside the loop. Let’s see the implementation:


actors.forEach(new Consumer<String>() {
	public void accept(String actor) {
		System.out.println(actor);
	}
});

Here forEach method is given an Anonymous Inner Class of Consumer interface. This class has an accept(T t) method which is being invoked for all the values of the collection. This code suffers from a design flaw where a new class is created each time we use forEach by passing the anonymous inner class.


ForLoopEvolution$1.class
ForLoopEvolution.class

This solution looks more verbose and complex than the earlier loops. Let’s try and refactor this in a simplified manner. The entire functional interface implementation can be written as a Lambda function, which is more intuitive. Let’s see this in action:


actors.forEach((e) -> {
 System.out.println(e);
 });

For each element e, the actor is being displayed in the console. This code can be simplified further by removing unnecessary curly braces and brackets.


actors.forEach(e -> System.out.println(e));

The main advantage of using lambda instead of the anonymous inner class is that there’s no bytecode pollution, no class creation, instead, the call to the lambda is deferred until runtime. Let’s see the bytecode for more details:
java invokedynamic

invoke dynamic helps the JVM to create the bytecode from lambda expression and delay the execution until runtime.

Method Inference

Method Inference further removes some more ceremony & verbosity. Let’s refactor to pass method inference instead of lambda:


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

One additional advantage of using the internal iterator is, the code is almost ready for parallelism. In Java 8 with the introduction of the stream api, we can run the code in parallel without the need of synchronization. A parallel stream can be created from the collection to process our functionality.


actors.parallelStream().forEach(System.out::println);

Summary

Internal Iterators are less complex & less error-prone, better maintainable. Code with an internal iterator usage can easily be made parallel.

Code Link: GitHub URL for Java Code

Leave a Reply

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

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages