Tuesday, May 08, 2018

Five for loops in Java

  1. List of strings
  2. Traditional for loop
  3. Enhanced for loop from Java 5
  4. forEach lambda from Java 8
  5. Stream.iterate with one lambda from Java 8
  6. Stream.iterate with two lambdas from Java 9
  7. All five for loops together

The main conceptual difference between for and while loops is that a for loop uses some form of index or counting variable to track what row, record or iteration you are up to whereas a while loop does not.

Here are five ways to use "for loops" in Java.

To top of post.

List of strings

I start out with a list of strings.

final List<String> strings = Arrays.asList("one", "two", "three", "four", "five");

To top of post.

Traditional for loop

The traditional for loop allows us to specify three things:

  1. The seed value: the initial value to give our index. The seed is 0 in this example.
  2. The hasNext condition (a.k.a. exit condition): a boolean expression to answer the question should we continue? In our example, we continue as long as index is less than the size of the strings list.
  3. The next value: an expression to generate the next index. In our example, we get next by adding 1 to index.
System.out.printf("Old school for loop.%n");
for (int index = 0; index < strings.size(); index++) {
   System.out.printf("Next string is %s%n", strings.get(index));
}

To top of post.

Enhanced for loop from Java 5

In Java 5, we got the enhanced for loop, which feels more like a while loop because we don't have an index any more. The enhanced for loop specifically helps us in situations where you are iterating over all items in a collection. You don't need to specify seed, hasNext or next: they are all implied. Start with the first element, end with a last element and make sure we loop over each element in between.

System.out.printf("%nEnhanced for loop from Java 5.%n");
for (final String string : strings) {
   System.out.printf("Next string is %s%n", string);
}

To top of post.

forEach lambda from Java 8

Java 8 gave us lambdas and a new way to loop over elements. Again, this feels more like a while in some ways because we don't have an index.

Just like the enhanced for loop, it is perfect for situations where you have a collection and you want to do something with each element. Any collection in Java has forEach (through the java.lang.Iterable interface).

System.out.printf("%nThe forEach lambda from Java 8.%n");
strings.forEach(string -> System.out.printf("Next string is %s%n", string));

Java 8 introduced java.util.stream.Stream and gave us a whole new way to think about looping. Instead of thinking about how to formulate a while or for loop (by specifying seed and exit conditions), with streams we focus on defining the set of values (a stream) that we want to operate on. Stream also has forEach, so we start thinking more about "iterating over a stream of values".

To top of post.

Stream.iterate with one lambda from Java 8

Stream.iterate provides a way to use for loop ideas with a stream of values.

System.out.printf("%nUsing Stream.iterate with one lambda from Java 8.%n");
Stream.iterate(0, index -> index + 1)
   .limit(strings.size())
   .forEach(index -> {
         System.out.printf("Next string is %s%n", strings.get(index));
});

This version of iterate() has only two arguments, allowing us to specify:

  1. The seed value: the initial value to give our index. The seed is 0 in this example.
  2. The next value: an expression to generate the next index. In our example, we get next by adding 1 to index.

We still need a way to provide the exit condition though, so I have added a limit() call from the Stream API.

To top of post.

Stream.iterate with two lambdas from Java 9

Stream.iterate in Java 9 got overloaded to give a way to specify all three things we use in a traditional for loop.

System.out.printf("%nUsing Stream.iterate with two lambdas from Java 9.%n");
Stream.iterate(0, index -> index < strings.size(), index -> index + 1).forEach(index -> {
   System.out.printf("Next string is %s%n", strings.get(index));
});

Now we can give:

  1. The seed value: the initial value to give our index. The seed is 0 in this example.
  2. The hasNext condition (a.k.a. exit condition): a boolean expression to answer the question should we continue? In our example, we continue as long as index is less than the size of the strings list.
  3. The next value: an expression to generate the next index. In our example, we get next by adding 1 to index.

To top of post.

All five for loops together

In summary, the techniques all together are below.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public final class Scrapbook {

   public static void main(final String[] args) {
      final List<String> strings = Arrays.asList("one", "two", "three", "four", "five");

      System.out.printf("Old school for loop.%n");
      for (int index = 0; index < strings.size(); index++) {
         System.out.printf("Next string is %s%n", strings.get(index));
      }

      System.out.printf("%nEnhanced for loop from Java 5.%n");
      for (final String string : strings) {
         System.out.printf("Next string is %s%n", string);
      }

      System.out.printf("%nThe forEach lambda from Java 8.%n");
      strings.forEach(string -> System.out.printf("Next string is %s%n", string));

      System.out.printf("%nUsing Stream.iterate with one lambda from Java 8.%n");
      Stream.iterate(0, index -> index + 1).limit(strings.size()).forEach(index -> {
         System.out.printf("Next string is %s%n", strings.get(index));
      });

      System.out.printf("%nUsing Stream.iterate with two lambdas from Java 9.%n");
      Stream.iterate(0, index -> index < strings.size(), index -> index + 1).forEach(index -> {
         System.out.printf("Next string is %s%n", strings.get(index));
      });

   }
}

Which gives the following output.

Old school for loop.
Next string is one
Next string is two
Next string is three
Next string is four
Next string is five

Enhanced for loop from Java 5.
Next string is one
Next string is two
Next string is three
Next string is four
Next string is five

The forEach lambda from Java 8.
Next string is one
Next string is two
Next string is three
Next string is four
Next string is five

Using Stream.iterate with one lambda from Java 8.
Next string is one
Next string is two
Next string is three
Next string is four
Next string is five

Using Stream.iterate with two lambdas from Java 9.
Next string is one
Next string is two
Next string is three
Next string is four
Next string is five

To top of post.