使用Collectors.toMap处理重复key需显式指定mergeFunction:(v1,v2)->v1保留首个值,(v1,v2)->v2保留末个值;忽略该参数将抛IllegalStateException;也可用groupingBy实现更灵活的聚合。

使用 Collectors.toMap 时,若流中存在重复的 key,默认会抛出 IllegalStateException。要实现“自动剔除重复 key”,关键不是真的“删除”,而是**显式指定冲突策略**——即当 key 冲突时,保留哪一个 value(比如保留第一个、最后一个,或按某种规则合并)。
用 mergeFunction 参数处理 key 冲突
Collectors.toMap(keyMapper, valueMapper, mergeFunction) 的第三个参数就是解决重复 key 的核心。它接收两个同 key 的 value,返回最终保留的 value。
- 保留第一个 value(跳过后续重复):
(v1, v2) -> v1 - 保留最后一个 value(覆盖之前):
(v1, v2) -> v2 - 自定义逻辑,如拼接字符串:
(v1, v2) -> v1 + ", " + v2
常见误操作:忽略 mergeFunction 导致异常
如果只写前两个参数:toMap(Person::getName, Person::getAge),而集合中有两个 Person 名字相同,就会立即抛异常。这不是“未剔除”,而是 Java 拒绝静默覆盖——必须由你明确决定如何处理。
例如:
List// ❌ 抛异常
Map
// ✅ 保留最后一个 Alice 的年龄
Map
进阶:结合 filtering 预先去重(按需)
如果业务上“重复 key”代表脏数据,且你只想保留首次出现的元素(而非仅 value),可先用 distinct() 或 filter() 配合 Set 记录已见 key:
- 简单场景(对象本身可去重):
stream.distinct().collect(toMap(...)) - 复杂场景(按 key 去重):用
filter+ConcurrentHashMap.newKeySet()或静态 Set(注意线程安全)
替代方案:用 groupingBy + mapping 灵活控制
若需更复杂的聚合(如取最大值、最小值、列表等),Collectors.groupingBy 更合适:
- 取每个 key 对应的最大 age:
groupingBy(Person::getName, collectingAndThen(maxBy(comparing(Person::getAge)), Optional::get)) - 收集所有同名人的 age 列表:
groupingBy(Person::getName, mapping(Person::getAge, toList()))
这类方式天然支持重复 key,不依赖 mergeFunction,语义也更清晰。










