Как превратить список в Map
Короткий ответ
Collectors.toMap() принимает две функции: как получить ключ и как получить значение:
record User(int id, String name) {}
void main() {
var users = List.of(new User(1, "Anna"), new User(2, "Oleg"));
Map<Integer, String> names = users.stream().collect(Collectors.toMap(User::id, User::name));
IO.println(names.get(2));
}Результат:
OlegОбъект целиком как значение
Function.identity() возвращает сам элемент — так строят индекс по ключу:
record User(int id, String name) {}
void main() {
var users = List.of(new User(1, "Anna"), new User(2, "Oleg"));
Map<Integer, User> byId = users.stream().collect(Collectors.toMap(User::id, Function.identity()));
IO.println(byId.get(1));
}Результат:
User[id=1, name=Anna]Повторяющиеся ключи и порядок
Третий аргумент решает, какое значение оставить при совпадении ключей, а четвёртый задаёт тип Map:
void main() {
var words = List.of("apple", "avocado", "banana", "blueberry", "cherry");
var longestByLetter = words.stream().collect(Collectors.toMap(
w -> w.charAt(0),
w -> w,
(a, b) -> a.length() >= b.length() ? a : b,
LinkedHashMap::new));
IO.println(longestByLetter);
}Результат:
{a=avocado, b=blueberry, c=cherry}Частая ошибка
Не учесть повторяющиеся ключи. Без функции слияния toMap() выбрасывает исключение:
void main() {
var words = List.of("apple", "avocado");
words.stream().collect(Collectors.toMap(w -> w.charAt(0), w -> w));
}Результат:
Exception in thread "main" java.lang.IllegalStateException: Duplicate key a (attempted merging values apple and avocado)Ещё одна ловушка: toMap() не принимает null в качестве значения и выбрасывает NullPointerException.
Функции из этого ответа
- Collectors.toMap() — Сборщик, который строит словарь из элементов потока.
- Function — Функция, которая принимает значение типа `T` и возвращает значение типа `R`.
- Collectors.groupingBy() — Сборщик, который раскладывает элементы по группам с общим ключом.