I wrote about Java Streams before, sharing how they work for me and how, in conjunction with Java's functional interfaces, they enable us to write clean(er) code. I'd like to revisit my learnings, with some focus on the final step: what happens at the tail end of a stream operation
There are four activities around Java Streams:
Create: There are numerous possibilities to create a stream. The most prevalent, I found, is
Collection.stream()which returns a stream of anything in Java's collection framework: Collections, Lists, Sets etc.
There are more possibilities provided by the Stream interface, the StreamBuilder interface, the StreamSupport utility class or Java NIO's Files (and probably some more)
Select: You can
sorted(). All those methods don't change individual stream members, but determine what elements will be processed further. Selection and manipulation can happen multiple times after each other
Manipulate: Replace each member of the stream with something else. That "something" can be the member itself with altered content. Methods that are fluent fit here nicely (like
stream().map(customer -> customer.setStatus(newStatus)). We use
flatMap()for this step. While it is perfectly fine to use Lambda Expressions, consider moving the Lambda body into its own function, to improve reading and debugging
Collect: You can "collect" a stream once. After that it becomes inaccessible. The closest to classic loops here is the
forEach()method, that allows you operate on the members as you are used to from the Java Collection framework.
Next are the convenience methods:
A typical way to use
collect()is in conjunction with the Collectors static class, that provides the most commonly needed methods like
groupingBy(). Check the JavaDoc, there are 37 methods at your disposal.
However, sometimes, you might have different needs for your code, there custom collectors shine