Collectors.toMap的第三参数mergeFunction用于解决键冲突,如(oldValue, newValue) -> newValue实现覆盖、-> oldValue实现保留首值、Integer::sum实现累加等,需避免副作用和空指针。
使用 collectors.tomap 时,若流中多个元素映射到相同键,默认会抛出 illegalstateexception: duplicate key。第三参数(merge function)正是用来定义“键冲突时如何合并值”,是解决该问题的核心手段。
理解 toMap 的三个核心参数
Collectors.toMap(keyMapper, valueMapper, mergeFunction) 中:
-
keyMapper:决定 Map 的键,如
Person::getId -
valueMapper:决定 Map 的值,如
Person::getName - mergeFunction:当键重复时,接收旧值(existing)和新值(incoming),返回最终保留的值
常见 mergeFunction 实战写法
根据业务需求选择合适的合并策略:
-
保留后出现的值:
(oldValue, newValue) -> newValue(覆盖) -
保留先出现的值:
(oldValue, newValue) -> oldValue(忽略后续) -
合并为列表:
(list, item) -> { list.add(item); return list; },需配合Collectors.collectingAndThen或初始化为ArrayList::new -
数值累加:
Integer::sum或(a, b) -> a + b(适用于统计场景) -
取最大/最小值:
Math::max或Comparator.naturalOrder().max()
避坑:mergeFunction 中避免副作用与空指针
mergeFunction 应是纯函数,不修改入参对象,且需主动处理 null:
- ❌ 错误示例:
(a, b) -> a.append(b)(修改了 a 的状态,可能引发并发或逻辑错误) - ✅ 正确示例:
(a, b) -> a + ", " + b(无副作用,返回新对象) - ⚠️ 注意 null:若 value 可能为 null,建议先用
Objects.toString(v, "")或判空处理
完整可运行示例
将用户列表按部门分组,同一部门只保留最新入职者姓名(按 id 模拟时间顺序):
List<User> users = Arrays.asList(
new User(1, "张三", "tech"),
new User(2, "李四", "hr"),
new User(3, "王五", "tech")
);
Map<String, String> deptToLatestName = users.stream()
.collect(Collectors.toMap(
User::getDept, // key: 部门
User::getName, // value: 姓名
(oldName, newName) -> newName // 冲突时取后者(即最新)
));
// 结果:{"tech"="王五", "hr"="李四"}

















