java 8 的 merge() 方法可一行代码解决计数累加、多字段合并、并发统计及 map 合并等场景,通过 key、value 和 remappingfunction 三参数实现键存在时计算新值、不存在时直接插入,返回合并后值或 null(触发删除),显著提升简洁性、可读性与线程安全性。

解决计数累加:一行替代判空+取值+赋值
统计类场景最常见,比如项目投入率汇总、用户行为频次、订单金额分年累加。传统写法要先判断 key 是否存在,再 get 原值,再做加法,最后 put 回去——三步操作重复调用 get(),还容易漏掉 null 判断。
用 merge() 只需一行:
-
countMap.merge(year, 1, Integer::sum)→ 年份首次出现就设为 1,再次出现就 +1 -
amountMap.merge(dept, amount, BigDecimal::add)→ 金额用不可变类型,避免浮点误差 - 若初始值不是 1(比如按权重累加),可写成
merge(key, weight, (old, w) -> old + w)
处理多字段业务合并:不覆盖、不丢值、按规则合
当需要把新数据“有选择地”合并进已有 Map,而不是简单覆盖(putAll)时,merge() 的 remappingFunction 就是核心控制点。
- 保留旧值不更新:
(old, newVal) -> old - 只取最大值(如记录最高评分):
(old, newVal) -> Math.max(old, newVal) - 拼接非空字符串(如日志标签追加):
(old, newVal) -> old == null ? newVal : old + "," + newVal - 清空某键(满足条件后主动删除):
(old, newVal) -> shouldRemove ? null : old(返回null会触发remove(key))
应对并发统计:用 ConcurrentHashMap + merge 保原子性
普通 HashMap.merge() 只简化逻辑,不解决线程安全;但换成 ConcurrentHashMap,merge() 底层由 CAS 或锁保障「查-算-存」一步完成,彻底避免多线程下计数丢失。
注意两点关键约束:
-
ConcurrentHashMap不接受null的 key 或 value,传入即抛NullPointerException - 若业务中可能含
null值,应先过滤(如stream().filter(Objects::nonNull)),或改用compute()
合并两个 Map:按需定制冲突策略
不同于 putAll() 的暴力覆盖,merge() 遍历源 Map 每个 entry,对每个 key 显式定义“新旧怎么合”。适合配置合并、报表补全、灰度数据对齐等场景。
- 以新为准(覆盖):
srcMap.forEach((k, v) -> target.merge(k, v, (old, newVal) -> newVal)) - 以旧为准(跳过新值):
(old, newVal) -> old - 数值型合并(如双端统计相加):
Integer::sum或自定义加权逻辑 - 对象合并(如 User 属性):需封装合并逻辑,例如
(old, newVal) -> old.merge(newVal)(前提是对象提供合并方法)










