How to split a list in two by a condition
Short answer
partitioningBy() creates a Map with two keys: true for elements that match the condition and false for the rest:
void main() {
var numbers = List.of(1, 2, 3, 4, 5, 6, 7);
Map<Boolean, List<Integer>> parts = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
IO.println(parts.get(true));
IO.println(parts.get(false));
}Output:
[2, 4, 6]
[1, 3, 5, 7]With counting
The second argument processes each part — for example, counts the elements:
record Student(String name, int score) {}
void main() {
var students = List.of(new Student("Anna", 85), new Student("Oleg", 40), new Student("Ivan", 62));
var passed = students.stream()
.collect(Collectors.partitioningBy(s -> s.score() >= 50, Collectors.counting()));
IO.println(passed);
}Output:
{false=1, true=2}Both groups always exist
Unlike groupingBy(), both parts are present even when one of them is empty:
void main() {
var numbers = List.of(2, 4, 6);
IO.println(numbers.stream().collect(Collectors.partitioningBy(n -> n > 10)));
}Output:
{false=[2, 4, 6], true=[]}Common mistake
Filtering the list twice — once by the condition and once by its opposite. That is two passes and a duplicated condition that is easy to change in only one place. partitioningBy() does it all in one pass.
Functions used in this recipe
- Collectors.partitioningBy() — Splits stream elements into two groups by a condition.
- Collectors.groupingBy() — A collector that sorts elements into groups that share a key.
- Collectors.counting() — A counting collector, usually used inside `groupingBy()`.