How to convert a list to a map
Short answer
Collectors.toMap() takes two functions: how to get the key and how to get the value:
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));
}Output:
OlegThe whole object as the value
Function.identity() returns the element itself — that is how you build an index by key:
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));
}Output:
User[id=1, name=Anna]Duplicate keys and order
The third argument decides which value to keep when keys collide, and the fourth sets the Map type:
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);
}Output:
{a=avocado, b=blueberry, c=cherry}Common mistake
Not accounting for duplicate keys. Without a merge function, toMap() throws:
void main() {
var words = List.of("apple", "avocado");
words.stream().collect(Collectors.toMap(w -> w.charAt(0), w -> w));
}Output:
Exception in thread "main" java.lang.IllegalStateException: Duplicate key a (attempted merging values apple and avocado)Another trap: toMap() does not accept null values and throws NullPointerException.
Functions used in this recipe
- Collectors.toMap() — A collector that builds a map from stream elements.
- Function — A function that takes a value of type `T` and returns a value of type `R`.
- Collectors.groupingBy() — A collector that sorts elements into groups that share a key.