DEV Community

Bertil Muth
Bertil Muth

Posted on

The joy of streams

Recently, I have written a class that looked roughly like this:

public class Requirement { ...
    public Requirement(...) { ...
    }
    public When getWhen() { ...
    }
    public Event getEvent() {...
    }
    public SystemReaction getSystemReaction() {...
    }
}
Enter fullscreen mode Exit fullscreen mode

I had a list of Requirement instances. Now, I needed the set of Event instances that was referenced by at least one Requirement. An Event instance could also be null.

In Java before Java 8, I would have done the following:
a) Create an empty HashSet
b) Iterate over the list of Requirement instances
c) For each Requirement instance, check if getEvent() returns null
d) If it does not return null, add the event to the HashSet

Starting with Java 8, you can use streams to accomplish this task in a one-liner:

Set<Event> events = requirements.stream().map(req -> req.getEvent())
  .filter(event -> event != null).collect(Collectors.toSet());
Enter fullscreen mode Exit fullscreen mode

To find out more about streams, check out this tutorial.

Latest comments (6)

Collapse
 
pawelsalawa profile image
Pawel Salawa • Edited

I love Java's streams, but still there is one thing which I hate about them. Verbosity where it's really not necessary and I see just one case where it's not necessary - for collectors. Every time we want to terminate a stream by getting collection from it, we had to do this ugly "collect(Collectors.toSomthieng())". I know, I know. It enables us to provide our custom collectors, but I don't see why Stream class does not have simple method "toList()" and "toSet()". These are the most common cases! Have a look: "stream.collect(Collectors.toList())" vs "stream.toList()". I didn't do any research, but I bet toList and toSet operations are like 80% of cases. Why not make this simpler? We still could use collect() for more customization, but lack of toList() and toSet() in the Stream is such a pain! Having such methods comes directly from intuition. These are the first which people look like when they learn Stream. It's not lost, since we have default methods and future Java versions may introduce these methods. I hope so. Maybe there's already such proposal and I just couldn't find it?

Collapse
 
bertilmuth profile image
Bertil Muth

I understand your pain :-) As far as I know, the vavr library tries to solve that problem.

Collapse
 
alainvanhout profile image
Alain Van Hout

I tend to write this in the following way, as it optimizes for clarity:

Set<Event> events = requirements.stream()
   .map(Requirement::getEvent)
   .filter(Objects::nonNonNull)
   .collect(Collectors.toSet());
Collapse
 
jeyj0 profile image
Jannis Jorre

You could even replace .filter(event -> event != null) with .filter(Objects::nonNull) !

Another awesome note: when calling .parallel() on a stream, every further task in the pipeline gets executed in parallel using the default forkjoinpool, which uses exactly the number of cores available minus 1 (for the main thread). Awesome for speeding up tasks in some situations!

Collapse
 
bertilmuth profile image
Bertil Muth

You're right. I created this based on an example with deeper nesting, and kept it in as I thought it might be easier to grasp for somebody new to the topic.

Collapse
 
jeyj0 profile image
Jannis Jorre

(1) I assumed it was because of a reason like that, but still wanted to give a hint for those who might appreciate it. I'd consider it a cleaner and more readable solution.

(2) This is definitely for more advanced users, but is such a powerfull tool, that I thought it should definitely be mentioned.