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 code which is more readable. 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
But you have to remember one thing: every step in the stream will be called until an item is rejected.
Filter first, map later
How many operations will be called in this code?
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.
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.
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.
Things to consider when using streams:
- If necessary, prepare data to be more easily filterable.
- Filter first, if possible. Fewer items equals fewer operations along the way.
- If not possible to filter first, try to use cheaper operations first, filter, then more expensive ones.