使用Collectors.toMap处理重复key时,需提供mergeFunction参数定义冲突时的“最优值”选取策略,如取最新时间戳、最大数值或按优先级排序,并注意null安全处理。

使用 Collectors.toMap 时,若流中存在重复 key,默认会抛出 IllegalStateException。要按业务规则“选取最优值”而非报错,关键在于提供第三个参数——mergeFunction,它定义当 key 冲突时如何从两个候选 value 中选出一个。
明确“最优”的业务含义
“最优”不是通用概念,必须由你定义。常见场景包括:
- 取最新时间戳对应的对象(如用
updatedAt字段比较) - 取数值最大的 value(如评分、权重、版本号)
- 取非 null 或非空字符串优先
- 按自定义优先级枚举排序(如
STATUS.DRAFT )
基础写法:传入 mergeFunction
语法结构为:Collectors.toMap(keyMapper, valueMapper, (v1, v2) -> { /* 选v1还是v2?*/ })
示例:按用户 ID 聚合,冲突时保留更新时间更晚的用户信息
Map<Long, User> latestUserMap = users.stream()
.collect(Collectors.toMap(
User::getId,
Function.identity(),
(u1, u2) -> u1.getUpdatedAt().isAfter(u2.getUpdatedAt()) ? u1 : u2
));封装可复用的合并策略
避免每次写冗长 lambda,可提取为静态方法或工具函数:
// 取字段值更大的那个
public static <T, U extends Comparable<? super U>>
BinaryOperator<T> maxBy(Function<T, U> extractor) {
return (t1, t2) -> extractor.apply(t1).compareTo(extractor.apply(t2)) >= 0 ? t1 : t2;
}
<p>// 使用
Map<Long, User> map = users.stream()
.collect(toMap(User::getId, Function.identity(), maxBy(User::getScore)));
注意 null 安全与边界情况
mergeFunction 中需主动处理 null:
- 如果 value 可能为 null,先判空再比较,否则
NullPointerException - 若业务要求“有则覆盖,无则保留”,可用
Objects.nonNull(v1) ? v1 : v2 - 若 keyMapper 或 valueMapper 可能返回 null,需提前过滤或映射默认值,否则会触发
NullPointerException

















