Alternative 2 looks best to me in this case
1. Readability
2. Performance
From Stream.toList()
Implementation Note:
Most instances of Stream will override this method and provide an implementation that is highly optimized compared to the implementation in this interface.
We benefit from optimization from JDK by using Stream API.
And in this case, using forEach and adding element one by one will be slower, especially when the list is large. It is because ArrayList
will need to extend it capacity whenever the list full, while Stream implementation ImmutableCollections.listFromTrustedArrayNullsAllowed
just store the result array into ListN
.
One more point to note about parallelism:
From Stream#forEach
The behavior of this operation is explicitly nondeterministic. For parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism. For any given element, the action may be performed at whatever time and in whatever thread the library chooses. If the action accesses shared state, it is responsible for providing the required synchronization.
numbersList.stream().parallel()
.forEach(i -> {
if(i % 6 != 0) {
return;
}
secondInts.add(i);
});
Will provide unexpected result, while
List<Integer> third = numbersList.stream().parallel()
.filter(i -> i % 6 == 0).sorted().forEach()
.toList();
is totally fine.
3. Flexibility
Imagine you want the filtered list to be sorted, in forEach approach, you can do it like:
numbersList.stream().sorted().
.forEach(i -> {
if(i % 6 != 0) {
return;
}
secondInts.add(i);
});
Which is much slower compared to
numbersList.stream()
.filter(i -> i % 6 == 0)
.sorted()
.toList();
As we need to sort the whole numbersList instead of filtered.
Or if you want to limit your result to 10 elements, it is not straight forward to do so with forEach, but just as simple as adding limit(10)
when using stream.
4. Less error prone
Stream API usually return Immutable object by default.
From Stream.toList()
Implementation Requirements:
The implementation in this interface returns a List produced as if by the following:
Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())))
Meaning that the returned list is immutable by default. Some advantages of immutability are:
- You can safely pass the list around to different method without worrying the list is modified.
- Immutable list are thread safe.
Read Pros. / Cons. of Immutability vs. Mutability for further discussion.