In Java 8's Streams, I know how to filter a collection based on a predicate, and process the items for which the predicate was true. What I'm wondering is, if the predicate divides the collection into only two groups, is it possible through the API to filter based on the predicate, process the filtered results, then immediately chain on a call to process all elements excluded by the filter?
For instance, consider the following List:
List<Integer> intList = Arrays.asList(1,2,3,4);
Is it possible to do:
intList.stream()
.filter(lessThanThree -> lessThanThree < 3)
.forEach(/* process */)
.exclusions(/* process exclusions */); //<-- obviously sudocode
Or would I have to just do the forEach
process for the filtered items and then call stream()
and filter()
on the original list to then process the remaining items?
Thanks!
Take a look at Collectors.partitioningBy
, which receives a predicate as an argument. You must use Stream.collect
to use it.
The result is a map with only two Boolean
entries: for the true
key, you have a List
containing the elements of the stream that matched the predicate, while for the false
key, you have a List
containing the elements that didn't match.
Note that Collectors.partitioningBy
has an overloaded version that accepts a second argument, which is a downstream collector you can use if you want each partition to be collected to something different than a list.
In your example, you could use it this way:
Map<Boolean, List<Integer>> partition = intList.stream()
.collect(Collectors.partitioningBy(i -> i < 3));
List<Integer> lessThan = partition.get(true); // [1, 2]
List<Integer> notLessThan = partition.get(false); // [3, 4]
I would also like to highlight an important observation, taken from a comment added by user @Holger in the linked question:
Collectors.partitioningBy
will always have a result fortrue
andfalse
in theMap
, i.e. an empty list if no element fell into that group, rather thannull
.
This particularity has been added to jdk9 docs as an API Note (again, this was observed by user @Holger):
If a partition has no elements, its value in the result Map will be an empty List.
There is no operation in the Stream interface to access excluded items. You can however implement a more complex predicate:
intList.stream()
.filter(i -> {
if (i < 3) return true;
else {
// Process excluded item i
return false;
}
}).forEach(/* Process included item. */)
But you must be careful to keep the predicate non-interfering and stateless.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With