Java Streams: Order Matters
Streams are one of my most used features of Java 8, especially in combination with lambdas. They make code more concise, and we all love shorter, more readable code. But a lot can go wrong along the way.
The beauty of Java streams is the ability to concat multiple operations to a “pipeline”.
It can replace most for
-loops in your code, especially the ones that are just pushing data from one data structure to another (e.g., from List<YourObject>
to Map<String, YourObject>
).
But you have to remember one thing: every step in the stream will be called until an item is rejected.
Table of Contents
Filter first, map later
How many operations will this code call?
This code will run map
5 times, sorted
8 times, filter
5 times, and forEach
2 times.
We got 20 operations to output 2 values.
That’s ridiculous!
Well, we can do better than that:
By filtering first, we are going to restrict the map/sorted
operations to a minimum:
filter
5 times, map
2 times, sort
1 time, and forEach
2 times, which saves us 10 operations in total.
In this example, it might seem like not a big deal.
But usually, we deal with more than just 5 items.
And the map
operation might be expensive to do, so doing less is always better.
Prepare first, then filter, and do stuff later
What if you can’t filter first? Well, then, it often helps to prepare your data in the first step just a little so you can filter in the next step and only do the heavy processing on the remaining items.
This way, you can reduce the heavy operations to be only applied to the objects that actually need it.
Conclusion
Things to consider when using streams:
- If necessary, prepare data to be more easily filterable.
- Filter first, if possible. Fewer items equal fewer operations along the way.
- If not possible to filter first, try to use cheaper operations first, filter, then more expensive ones.