Convert a List to a Map Using Streams in Java
Problem
Collectors.toMap turns each stream element into one map entry using two functions: one that produces the key and one that produces the value.
Given a list of fruit names, build a map from each fruit to the length of its name, keeping the list's order.
Java Program
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ListToMapStream {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date");
Map<String, Integer> lengths = fruits.stream()
.collect(Collectors.toMap(
Function.identity(), // key: the fruit itself
String::length, // value: its length
(a, b) -> a, // what to do if a key repeats (keep the first)
LinkedHashMap::new)); // keep the list's order
System.out.println(lengths);
}
}Output
Core Logic
The four-argument form of toMap spells out every decision: key, value, what to do on a duplicate key, and which map to build.
- 1
Function.identity()uses each fruit itself as the key. - 2
String::lengthcomputes the value. - 3
(a, b) -> ais the merge function, keeping the first value if a key repeats. - 4
LinkedHashMap::newkeeps entries in the order the fruits appeared.
banana and cherry both map to 6 — equal values are fine, only duplicate keys need the merge rule.Key Point: The plain two-argument toMap(keyFn, valueFn) returns a HashMap and throws on the first duplicate key — supply the extra arguments whenever duplicates or order matter.
Why: Each element is inserted into the map once, and the map holds up to n entries.
Key Concepts
Approach 2: Handling Duplicate Keys
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ListToMapDuplicates {
public static void main(String[] args) {
List<String> votes = Arrays.asList("red", "blue", "red", "green", "blue", "red");
try {
votes.stream().collect(Collectors.toMap(Function.identity(), v -> 1)); // no merge function
} catch (IllegalStateException e) {
System.out.println("Without a merge function: IllegalStateException (duplicate key)");
}
Map<String, Integer> tally = votes.stream()
.collect(Collectors.toMap(Function.identity(), v -> 1, Integer::sum, LinkedHashMap::new));
System.out.println("With Integer::sum: " + tally);
}
}
Output
Core Logic
When keys repeat, the merge function decides how to combine values — using Integer::sum turns toMap into a tally counter.
- 1The first attempt has no merge function, so the second
redtriggers anIllegalStateException, which is caught and reported. - 2The second attempt maps every vote to the value 1.
- 3
Integer::sumadds the existing and new values whenever a key repeats. - 4
LinkedHashMap::newkeeps colours in first-seen order.
red appears three times, so its 1s merge into 3; blue gets 2 and green 1.Key Point: For pure counting, groupingBy(..., counting()) reads more clearly — but the merge-function pattern also handles max, min or string joins.
Why: Each of the n votes triggers one map insert or merge, and the map holds one entry per distinct key k.
Key Concepts
Interview Questions
What happens if toMap meets a null value?
Collectors.toMap throws a NullPointerException for null values, because it uses Map.merge internally. Filter out nulls first, or collect with a loop.
How do you group values instead of keeping one per key?
Use Collectors.groupingBy(keyFunction), which collects every element with the same key into a list.