← Назад к списку
ПрограммированиеJava и KotlinJunior

Напишите через Stream API: посчитать частоту слов в списке и вывести топ-3 самых частых.

Короткий ответ

  • groupingBy по слову + Collectors.counting даёт карту частот
  • Нормализация toLowerCase до группировки
  • Для топ-3: stream по entrySet, сортировка по значению вниз, limit
  • comparingByValue().reversed() — готовый компаратор
  • Результат собрать в LinkedHashMap, чтобы сохранить порядок
  • Объяснить сложность: подсчёт O(n), сортировка O(k log k)

Задача на два шага: groupingBy + counting для частот, затем сортировка entrySet по значению с limit(3).

Как сказать вслух

пример ответа

Сначала группирую слова по самим себе со счётчиком — получаю карту «слово — количество». Перед этим привожу к нижнему регистру, чтобы «Java» и «java» считались одним словом. Потом беру стрим по записям карты, сортирую по значению по убыванию, ограничиваюсь тремя и собираю в LinkedHashMap, чтобы не потерять порядок. На словах важно оговорить, что обычный toMap порядок бы не сохранил.

Подробный ответ

Основной ответ

Решение в два конвейера. Первый: words.stream().collect(groupingBy(String::toLowerCase, counting())) — карта частот за O(n). Второй: по entrySet сортируем Map.Entry.comparingByValue(reverseOrder()) или .reversed(), берём limit(3) и собираем toMap с LinkedHashMap::new — обычный HashMap разрушил бы порядок сортировки, а это типовая ловушка. Полезные уточнения, которые ценит интервьюер: при равных частотах добавить вторичный компаратор по ключу для детерминизма; если вход — строка текста, предварительно split по регулярному выражению \\s+ и фильтр пустых; для больших данных топ-k эффективнее через кучу размера k, но для собеседования сортировки достаточно.

Ключевые моменты

  • groupingBy + counting. Каноническая пара для частот; downstream-коллектор считает элементы группы.
  • Сортировка карты. Map не сортируется сама — нужен stream по entrySet и компаратор по значению.
  • LinkedHashMap в toMap. Четырёхаргументный toMap с фабрикой — иначе порядок топа потеряется.
  • Крайние случаи. Регистр, пустые строки, ничья по частоте — проговорить до написания кода.

Практический контекст

Одна из самых частых разминочных задач на Java-собеседованиях: проверяет знание коллекторов глубже, чем map/filter. По той же схеме решаются «сгруппировать сотрудников по отделу», «средняя зарплата по городу» — меняется только downstream-коллектор. Сильный кандидат пишет решение сразу, проговаривает ловушку с порядком в toMap и предлагает обработку регистра и знаков препинания.

Пример кода

List<String> words = List.of("java", "Go", "java", "rust", "go", "java");

Map<String, Long> freq = words.stream()
    .collect(Collectors.groupingBy(String::toLowerCase,
                                   Collectors.counting()));

Map<String, Long> top3 = freq.entrySet().stream()
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed()
            .thenComparing(Map.Entry.comparingByKey()))
    .limit(3)
    .collect(Collectors.toMap(
        Map.Entry::getKey, Map.Entry::getValue,
        (a, b) -> a, LinkedHashMap::new));

System.out.println(top3); // {java=3, go=2, rust=1}

Частые ошибки

  • Собирают отсортированный результат в обычный HashMap и теряют порядок
  • Забывают merge-функцию в toMap и получают исключение на дубликатах
  • Пытаются отсортировать Map без стрима по entrySet, утверждая, что sorted применится к карте

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru