Как разделить список на две части по условию
Короткий ответ
partitioningBy() создаёт Map с двумя ключами: true — элементы, подходящие под условие, false — остальные:
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));
}Результат:
[2, 4, 6]
[1, 3, 5, 7]С подсчётом
Второй аргумент обрабатывает каждую часть — например, считает элементы:
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);
}Результат:
{false=1, true=2}Обе группы всегда есть
В отличие от groupingBy(), обе части есть даже тогда, когда одна из них пустая:
void main() {
var numbers = List.of(2, 4, 6);
IO.println(numbers.stream().collect(Collectors.partitioningBy(n -> n > 10)));
}Результат:
{false=[2, 4, 6], true=[]}Частая ошибка
Фильтровать список дважды — один раз по условию и второй раз по обратному. Это два прохода и повторённое условие, которое легко изменить только в одном месте. partitioningBy() делает всё за один проход.
Функции из этого ответа
- Collectors.partitioningBy() — Делит элементы потока на две группы по условию.
- Collectors.groupingBy() — Сборщик, который раскладывает элементы по группам с общим ключом.
- Collectors.counting() — Сборщик-счётчик; обычно используется внутри `groupingBy()`.