Introduction

For loops vs Streams in Java. Probably one of the most asked questions since Java 8 introduced the Streams API.

Today’s blog post will discuss the pros and cons and when to use them.

Performance comparison

Check out this Twitter’s thread comparing for loops with Streams as a reference.

As we can see there, Streams tend to perform quite slower than for loops. This is especially true when the operations carried out by either the Stream or the for loop are not that many.

If the amount of data is small, for loops will usually perform better than Streams.

It’s also worth noticing that the Streams API allows us to create parallel Streams without having to worry about the implementation details. Quick disclaimer: Make sure you need parallel Streams before actually using them and run some benchmarks.

Readability comparison

This is probably the most subjective topic. There are many old-school developers that will stick with for loops forever. On the other hand, there are also many others (including myself) that started working with Streams and were charmed by the readability they provide.

For loops

Let’s use an example, we want to iterate over an ArrayList of Strings that will have a maximum of 5 elements and we want to know the total length of all Strings combined. With for loops we could do something like:

private static int getTotalLengthOfElements(List<String> input) {
    int totalLength = 0;
    for (int i = 0; i < input.size(); i++) {
        totalLength += input.get(i).length();
    }
    return totalLength;
}

We can then make use of this method

public static void main(String[] args){
    List<String> input = List.of("What", "a", "bunch", "of", "Strings");
    int totalLengthOfStrings = getTotalLengthOfElements(input);
    System.out.println(totalLengthOfStrings);
}

That will print 19.

For loop result

Streams

The previous example could also be written with Streams as:

private static int getTotalLengthOfElements(List<String> input) {
    return input.stream()
            .mapToInt(String::length)
            .sum();
}

This will, once again output 19:

Streams result

Here we leverage the existence of the sum function provided by the IntStream interface.

In my opinion, even though this will probably perform slower than the for approach, this looks way better in terms of readability.

If the input data were to change to a higher amount of Strings, we could make use of the parallelStream just by calling the method:

private static int getTotalLengthOfElements(List<String> input) {
    return input.parallelStream()
            .mapToInt(String::length)
            .sum();
}

So we could say that this approach is also better in terms of adaptability.

Conclusion

We briefly discussed for loops vs Streams in Java.

I would say that if you’re working on a project with your team, you should decide with which approach you all feel more comfortable.

Most likely, performance won’t be an issue when using any of both so the most important thing to worry about is readability. And that is entirely up to you.

Quick reminder that if you’re a Java lover just like me, don’t miss the Java posts I’ll be uploading to this blog.