toMap, joining и summarizing
Преобразуйте потоки в карты, объединяйте строки с помощью joining и вычисляйте статистику с помощью summarizingInt.
«toMap, joining и summarizing» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Основы Collectors.toMap
Collectors.toMap(keyMapper, valueMapper) преобразует поток в Map. Каждый элемент предоставляет одну пару «ключ—значение».
import java.util.*;
import java.util.stream.*;
record Country(String code, String name) {}
List<Country> countries = List.of(
new Country("US","United States"),
new Country("DE","Germany"),
new Country("JP","Japan")
);
Map<String, String> codeToName =
countries.stream().collect(
Collectors.toMap(Country::code, Country::name)
);
System.out.println(codeToName.get("DE")); // GermanyОбработка повторяющихся ключей
toMap выбрасывает IllegalStateException при повторяющихся ключах. Передайте функцию объединения третьим аргументом, чтобы разрешить конфликт:
List<String> words = List.of("apple","ant","banana","bat","cherry");
Map<Character, String> firstByLetter =
words.stream().collect(
Collectors.toMap(
w -> w.charAt(0),
w -> w,
(existing, replacement) -> existing // keep first
)
);
System.out.println(firstByLetter); // {a=apple, b=banana, c=cherry}toMap с преобразованием значения
valueMapper может быть любой функцией — преобразуйте элемент перед сохранением:
record User(String login, String email) {}
List<User> users = List.of(
new User("alice","alice@example.com"),
new User("bob","bob@example.com")
);
Map<String, String> loginToEmail =
users.stream().collect(
Collectors.toMap(User::login, u -> u.email().toUpperCase())
);
System.out.println(loginToEmail.get("alice")); // ALICE@EXAMPLE.COMtoUnmodifiableMap
Используйте Collectors.toUnmodifiableMap(), чтобы создать неизменяемую карту. Это эквивалентно обёртке Collections.unmodifiableMap(), но выглядит чище:
Map<String, Integer> wordLengths =
Stream.of("Java","Stream","API")
.collect(Collectors.toUnmodifiableMap(w -> w, String::length));
// wordLengths.put("test",4); // throws UnsupportedOperationExceptionCollectors.joining
Collectors.joining() объединяет строковые элементы. Он принимает необязательные разделитель, префикс и суффикс:
List<String> names = List.of("Alice","Bob","Carol");
System.out.println(names.stream().collect(Collectors.joining()));
// AliceBobCarol
System.out.println(names.stream().collect(Collectors.joining(", ")));
// Alice, Bob, Carol
System.out.println(names.stream().collect(Collectors.joining(", ", "[", "]")));
// [Alice, Bob, Carol]joining для CSV-вывода
Создавайте строки CSV или SQL-условия IN с помощью joining:
List<Integer> ids = List.of(1,2,3,4,5);
String inClause = ids.stream()
.map(Object::toString)
.collect(Collectors.joining(",", "(", ")"));
System.out.println("WHERE id IN " + inClause);
// WHERE id IN (1,2,3,4,5)summarizingInt / Long / Double
Collectors.summarizingInt() вычисляет всю статистику за один проход и возвращает объект IntSummaryStatistics:
record Product(String name, int price) {}
List<Product> products = List.of(
new Product("A",100), new Product("B",200), new Product("C",150)
);
IntSummaryStatistics stats =
products.stream().collect(Collectors.summarizingInt(Product::price));
System.out.println("Count: " + stats.getCount()); // 3
System.out.println("Sum: " + stats.getSum()); // 450
System.out.println("Min: " + stats.getMin()); // 100
System.out.println("Max: " + stats.getMax()); // 200
System.out.println("Avg: " + stats.getAverage()); // 150.0summingInt и averagingInt
Для более простых случаев, когда требуется только одна статистика:
int total = products.stream().collect(Collectors.summingInt(Product::price));
System.out.println("Total: " + total); // 450
double avg = products.stream().collect(Collectors.averagingInt(Product::price));
System.out.println("Average: " + avg); // 150.0groupingBy с summarizingInt
Объединяйте группировку со сбором статистики для каждой группы:
record Sale(String region, int amount) {}
List<Sale> sales = List.of(
new Sale("North",100), new Sale("South",200),
new Sale("North",150), new Sale("South",300)
);
Map<String, IntSummaryStatistics> statsByRegion =
sales.stream().collect(
Collectors.groupingBy(Sale::region,
Collectors.summarizingInt(Sale::amount))
);
statsByRegion.forEach((r,s) ->
System.out.printf("%s: sum=%d avg=%.1f%n", r, s.getSum(), s.getAverage()));Сбор в LinkedHashMap
По умолчанию toMap создаёт HashMap без определённого порядка. Используйте перегрузку с четырьмя аргументами, чтобы указать LinkedHashMap и сохранить порядок вставки:
Map<String, Integer> ordered =
Stream.of("banana","apple","cherry")
.collect(Collectors.toMap(
w -> w,
String::length,
(a,b) -> a,
LinkedHashMap::new
));
System.out.println(ordered.keySet()); // [banana, apple, cherry]Практический пример: анализатор конфигурации
Разбирайте строки key=value в карту с помощью toMap:
List<String> lines = List.of("host=localhost","port=5432","db=myapp");
Map<String, String> config =
lines.stream()
.map(l -> l.split("=", 2))
.collect(Collectors.toMap(a -> a[0], a -> a[1]));
System.out.println(config.get("port")); // 5432Быстрая проверка
Вы используете Collectors.toMap(k, v) для потока, содержащего два элемента с одинаковым ключом. Что произойдёт?
Повторение: toMap, joining, summarizing
Основные выводы:
- toMap(keyMapper, valueMapper) — выбрасывает исключение при повторяющихся ключах без функции объединения
- joining(разделитель, префикс, суффикс) — элегантно объединяет строки
- summarizingInt за один проход возвращает количество, сумму, минимум, максимум и среднее значение
- Используйте summingInt/averagingInt, если нужна только одна статистика
- Используйте toMap с четырьмя аргументами и LinkedHashMap::new для упорядоченных результатов
Часто задаваемые вопросы
Урок «toMap, joining и summarizing» бесплатный?
Да — полный текст урока «toMap, joining и summarizing» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «toMap, joining и summarizing»?
Преобразуйте потоки в карты, объединяйте строки с помощью joining и вычисляйте статистику с помощью summarizingInt. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «toMap, joining и summarizing»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- groupingBy: классификация элементов
- partitioningBy и подсчёт
- toMap, joining и summarizing
- Создание собственного сборщика