Skip to content
The Code Storm
All articles
Stream Storm2 min read

Why Collectors.toMap throws "Duplicate key", and how to fix it

toMap refuses to guess which value you want when a key repeats. Give it a merge function and the crash goes away.

A collector decides the shape of your result: a map, two groups, or one string. toMap builds a lookup table that links each key to one value. That "one" is where the crash comes from.

The setup

Three students and their test scores:

java
record Score(String name, int score) {}
 
List<Score> scores = List.of(
    new Score("Asha", 82),
    new Score("Ravi", 45),
    new Score("Meena", 67));
 
Map<String, Integer> byName = scores.stream()
    .collect(Collectors.toMap(Score::name, Score::score));
// {Asha=82, Ravi=45, Meena=67}

The crash

Asha takes the test again and scores 90. Now her name appears twice:

java
List<Score> scores = List.of(
    new Score("Asha", 82), new Score("Ravi", 45),
    new Score("Meena", 67), new Score("Asha", 90));
 
scores.stream().collect(Collectors.toMap(Score::name, Score::score));
// IllegalStateException: Duplicate key Asha (attempted merging values 82 and 90)

A map keeps one value per key, and toMap won't guess which score you want.

The fix: a merge function

Pass a third argument. It decides what happens when a key repeats:

java
Map<String, Integer> best = scores.stream()
    .collect(Collectors.toMap(Score::name, Score::score, Math::max));
// {Ravi=45, Asha=90, Meena=67}

Math::max keeps the higher score. Other common choices:

  • (a, b) -> a keeps the first value
  • (a, b) -> b keeps the last value
  • Integer::sum adds them up

Bonus: partitioningBy and joining

partitioningBy always gives you exactly two groups, true and false:

java
Map<Boolean, List<String>> passed = scores.stream()
    .collect(Collectors.partitioningBy(
        s -> s.score() >= 50,
        Collectors.mapping(Score::name, Collectors.toList())));

joining glues strings into one, with an optional prefix and suffix:

java
Stream.of("Asha", "Ravi", "Meena").collect(Collectors.joining(", ", "[", "]"));
// [Asha, Ravi, Meena]

Get the next one in your inbox

New posts, videos and free PDFs. One email a week at most.