← Назад к вопросам
Подсчёт частоты слов с использованием Stream API
1.7 Middle🔥 251 комментариев
#Stream API и функциональное программирование#Основы Java
Условие
Используя Stream API, подсчитайте частоту каждого слова в тексте и верните Map<String, Long>.
Пример
Входные данные: "java is great and java is fun"
Выходные данные:
{java=2, is=2, great=1, and=1, fun=1}
Требования
- Используйте Stream API и Collectors
- Игнорируйте регистр
- Решение в одну строку (functional style)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Подсчёт частоты слов с Stream API
Это классическая задача на собеседовании, которая проверяет понимание Stream API, особенно groupingBy() коллектора.
Решение
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Arrays;
public class WordFrequency {
public static void main(String[] args) {
String text = "java is great and java is fun";
Map<String, Long> frequency = Arrays.stream(text.split(" "))
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
System.out.println(frequency);
// Вывод: {java=2, is=2, and=1, great=1, fun=1}
}
}
Разбор решения пошагово
1. Разделение текста на слова
text.split(" ") // Массив строк
// ["java", "is", "great", "and", "java", "is", "fun"]
2. Создание Stream из массива
Arrays.stream(text.split(" "))
// Stream<String>
3. Преобразование в нижний регистр
.map(String::toLowerCase)
// Stream<String> с «java», «is», «great»... (все нижнего регистра)
4. Группировка и подсчёт
.collect(Collectors.groupingBy(
word -> word, // Ключ (само слово)
Collectors.counting() // Значение (кол-во)
))
// Map<String, Long>
Детальный разбор groupingBy()
groupingBy() — это коллектор, который группирует элементы по ключу:
// Синтаксис
Collectors.groupingBy(
keyMapper, // Функция для извлечения ключа
downstream // Коллектор для значения (опционально)
)
Что происходит:
- Для каждого элемента вычисляется ключ с помощью
keyMapper - Элементы группируются по этому ключу
- Для каждой группы применяется
downstreamколлектор - Результат —
Map<Key, Value>
Альтернативные решения
С регулярными выражениями (более гибко)
import java.util.regex.Pattern;
String text = "java is great and java is fun";
Map<String, Long> frequency = Pattern.compile(" ")
.splitAsStream(text)
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
С собственной функцией извлечения
public class WordFrequency {
public static void main(String[] args) {
String text = "java is great and java is fun";
Map<String, Long> frequency = Arrays.stream(text.split(" "))
.map(String::toLowerCase)
.map(String::trim) // Удалить пробелы
.filter(word -> !word.isEmpty()) // Исключить пустые
.collect(Collectors.groupingBy(
Function.identity(), // Явно: слово -> слово
Collectors.counting()
));
System.out.println(frequency);
}
}
С сортировкой по частоте
import java.util.LinkedHashMap;
Map<String, Long> frequency = Arrays.stream(text.split(" "))
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
))
// Сортировка по значению (частоте) в убывающем порядке
.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new // Сохранить порядок
));
С игнорированием пунктуации
public class WordFrequencyAdvanced {
public static void main(String[] args) {
String text = "Java is great! And Java is fun."
Map<String, Long> frequency = Arrays.stream(text.split("\\s+"))
.map(word -> word.replaceAll("[^a-zA-Z]", "")) // Удалить не-буквы
.map(String::toLowerCase)
.filter(word -> !word.isEmpty()) // Исключить пустые
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
System.out.println(frequency);
// Вывод: {java=2, is=2, and=1, great=1, fun=1}
}
}
Вывод результата красиво
Map<String, Long> frequency = Arrays.stream(text.split(" "))
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
// Способ 1: Простая печать
System.out.println(frequency);
// Способ 2: Красивый вывод
frequency.forEach((word, count) ->
System.out.println(word + ": " + count)
);
// Способ 3: С форматированием
frequency.entrySet().stream()
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
.forEach(entry ->
System.out.printf("%s: %d%n", entry.getKey(), entry.getValue())
);
Полный пример с тестами
import java.util.Map;
import java.util.Arrays;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class WordFrequencyTest {
public static Map<String, Long> countWords(String text) {
return Arrays.stream(text.split(" "))
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
}
@Test
public void testBasicFrequency() {
String text = "java is great and java is fun";
Map<String, Long> result = countWords(text);
assertEquals(2, result.get("java"));
assertEquals(2, result.get("is"));
assertEquals(1, result.get("great"));
assertEquals(1, result.get("and"));
assertEquals(1, result.get("fun"));
}
@Test
public void testCaseInsensitive() {
String text = "Java JAVA java";
Map<String, Long> result = countWords(text);
assertEquals(1, result.size());
assertEquals(3, result.get("java"));
}
@Test
public void testEmptyString() {
String text = "";
Map<String, Long> result = countWords(text);
assertEquals(1, result.size());
// Один элемент - пустая строка
}
@Test
public void testSingleWord() {
String text = "java";
Map<String, Long> result = countWords(text);
assertEquals(1, result.size());
assertEquals(1, result.get("java"));
}
}
Объяснение для интервьюера
Почему этот подход хороший:
- Читаемость — код на функциональном стиле, понятно намерение
- Производительность — Streams оптимизированы и потокобезопасны
- Простота — одна строка вместо цикла с if/else
- Масштабируемость — легко добавить фильтры или преобразования
Сложность:
- Временная: O(n) — проходим по всем словам один раз
- Пространственная: O(k) — где k = количество уникальных слов
Вариант с игнорированием пунктуации и специальных символов
public static Map<String, Long> countWordsAdvanced(String text) {
return Arrays.stream(text.toLowerCase().split("[\\s\\p{Punct}]+"))
.filter(word -> !word.isEmpty())
.collect(Collectors.groupingBy(
word -> word,
Collectors.counting()
));
}
// Пример
String text = "Hello, world! Hello Java. Java is great.";
Map<String, Long> freq = countWordsAdvanced(text);
// {hello=2, world=1, java=2, is=1, great=1}
Заключение
Эта задача демонстрирует мастерство в:
- Stream API и терминальных операциях
- Collectors и особенно
groupingBy() - Функциональном стиле программирования
- Обработке строк
Ключевой моменты для ответа на собеседовании:
- Используй
groupingBy()сcounting()коллектором - Преобразуй текст в нижний регистр для case-insensitive
- Объясни, как работает
groupingBy()под капотом - Упомяни возможные улучшения (пунктуация, производительность)