0Pricing
Java Academy · Урок

partitioningBy и подсчёт

Разделяйте поток на две группы с помощью partitioningBy и подсчитывайте элементы с помощью counting.

«partitioningBy и подсчёт» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Основы partitioningBy

Collectors.partitioningBy(predicate) разделяет поток ровно на две группы — true и false — и возвращает Map<Boolean, List<T>>.

import java.util.*;
import java.util.stream.*;

List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,10);

Map<Boolean, List<Integer>> evenOdd =
    nums.stream().collect(Collectors.partitioningBy(n -> n % 2 == 0));

System.out.println(evenOdd.get(true));  // [2, 4, 6, 8, 10]
System.out.println(evenOdd.get(false)); // [1, 3, 5, 7, 9]

partitioningBy и filter

В отличие от filter, partitioningBy одновременно сохраняет обе группы — это удобно, когда нужны обе части разбиения.

record Student(String name, int score) {}
List<Student> students = List.of(
    new Student("Alice",90), new Student("Bob",55),
    new Student("Carol",72), new Student("Dave",48)
);

Map<Boolean, List<Student>> result =
    students.stream().collect(
        Collectors.partitioningBy(s -> s.score() >= 60)
    );
System.out.println("Passed: " + result.get(true).size());
System.out.println("Failed: " + result.get(false).size());

Последующий сборщик с partitioningBy

Объединяйте partitioningBy с последующим сборщиком так же, как groupingBy:

Map<Boolean, Long> passFailCount =
    students.stream().collect(
        Collectors.partitioningBy(
            s -> s.score() >= 60,
            Collectors.counting()
        )
    );
System.out.println("Passed: " + passFailCount.get(true));
System.out.println("Failed: " + passFailCount.get(false));

Сборщик counting()

Collectors.counting() подсчитывает количество элементов в потоке или последующей группе. Это удобный эквивалент reducing(0L, e -> 1L, Long::sum).

long count = Stream.of("a","bb","ccc","dddd")
    .collect(Collectors.counting());
System.out.println(count); // 4

// Same as:
long count2 = Stream.of("a","bb","ccc","dddd").count();
// Use counting() as a downstream; use .count() as a terminal op

counting() как последующий сборщик

counting() особенно полезен как последующий сборщик для groupingBy/partitioningBy:

List<String> words = List.of("cat","dog","car","door","cup","dune");

Map<Character, Long> byFirstLetter =
    words.stream().collect(
        Collectors.groupingBy(w -> w.charAt(0), Collectors.counting())
    );
byFirstLetter.forEach((c, n) -> System.out.println(c + ": " + n));
// c: 3, d: 3

Разделение на группы имён

Извлекайте имена из каждой группы с помощью mapping в качестве последующего сборщика:

Map<Boolean, List<String>> namesByPass =
    students.stream().collect(
        Collectors.partitioningBy(
            s -> s.score() >= 60,
            Collectors.mapping(Student::name, Collectors.toList())
        )
    );
System.out.println("Passed: " + namesByPass.get(true));
System.out.println("Failed: " + namesByPass.get(false));

Объединение counting и summarizingInt

Collectors.summarizingInt() за один проход вычисляет количество, сумму, минимум, максимум и среднее значение:

IntSummaryStatistics stats =
    students.stream().collect(
        Collectors.summarizingInt(Student::score)
    );
System.out.println("Count: " + stats.getCount());
System.out.println("Average: " + stats.getAverage());
System.out.println("Max: " + stats.getMax());
System.out.println("Min: " + stats.getMin());

Практический пример: разделение A/B-теста

Разделите пользователей на контрольную группу и группу воздействия для A/B-теста:

record User(String id, boolean isInTreatment) {}
List<User> users = List.of(
    new User("u1",true), new User("u2",false),
    new User("u3",true), new User("u4",false)
);

Map<Boolean, Long> split =
    users.stream().collect(
        Collectors.partitioningBy(User::isInTreatment, Collectors.counting())
    );
System.out.println("Treatment: " + split.get(true)); // 2
System.out.println("Control: " + split.get(false));  // 2

Карта частот с groupingBy + counting

Подсчитывайте количество появлений каждого элемента, например частоту слов:

List<String> words2 = List.of("apple","banana","apple","cherry","banana","apple");

Map<String, Long> freq =
    words2.stream().collect(
        Collectors.groupingBy(w -> w, Collectors.counting())
    );
freq.entrySet().stream()
    .sorted(Map.Entry.<String,Long>comparingByValue().reversed())
    .forEach(e -> System.out.println(e.getKey()+": "+e.getValue()));

partitioningBy всегда возвращает оба ключа

В отличие от groupingBy, partitioningBy всегда возвращает карту с обоими ключами — true и false, даже если одна из групп пуста. Это предотвращает NullPointerExceptions при вызове map.get(true).

List<Integer> allEven = List.of(2,4,6);
Map<Boolean, List<Integer>> m =
    allEven.stream().collect(Collectors.partitioningBy(n -> n % 2 != 0));
System.out.println(m.get(true));  // [] (empty, not null)
System.out.println(m.get(false)); // [2, 4, 6]

Производительность: один проход

И partitioningBy, и counting выполняются за один проход по потоку — O(n). Сортировка им не требуется. Поэтому они значительно эффективнее, чем два вызова filter или сортировка перед группировкой.

Быстрая проверка

Каков тип результата Collectors.partitioningBy(predicate)?

Повторение: partitioningBy и counting

Основные выводы:

  • partitioningBy разделяет элементы ровно на две группы: true и false
  • Оба ключа присутствуют всегда — в отличие от groupingBy, риск NPE отсутствует
  • counting() подсчитывает элементы; используйте его как последующий сборщик для групп
  • summarizingInt/Long/Double за один проход возвращает количество, сумму, минимум, максимум и среднее значение
  • Все эти операции выполняются за один проход по потоку O(n)

Часто задаваемые вопросы

Урок «partitioningBy и подсчёт» бесплатный?

Да — полный текст урока «partitioningBy и подсчёт» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «partitioningBy и подсчёт»?

Разделяйте поток на две группы с помощью partitioningBy и подсчитывайте элементы с помощью counting. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «partitioningBy и подсчёт»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. groupingBy: классификация элементов
  2. partitioningBy и подсчёт
  3. toMap, joining и summarizing
  4. Создание собственного сборщика
← Назад к Java Academy