Map.merge() 是 Java 8 提供的线程安全统计更新方法,通过“查-算-存”原子操作避免并发计数丢失;ConcurrentHashMap 中基于 CAS 或锁实现 UPSERT,支持灵活合并逻辑如累加、取最大值等,但要求 key/value 非 null。
Map.merge() 是 Java 8 引入的“一行解决统计更新”的核心方法,它天然支持键存在与否的统一处理,同时在 ConcurrentHashMap 中保证原子性,避免多线程下计数丢失。
为什么 merge 比传统写法更可靠?
传统统计常写成:
先 get(key),判断是否为 null再 put(key, old == null ? 1 : old + 1)但 get + put 非原子操作,在并发场景下可能被其他线程覆盖,导致计数少加merge 则把“查-算-存”封装为单次调用,ConcurrentHashMap 的 merge 实现底层基于 CAS 或锁机制,真正实现线程安全的 UPSERT(插入或更新)。
统计计数的典型写法
以单词频次统计为例:
countMap.merge("apple", 1, Integer::sum):key 不存在 → 直接存 1;存在 → 执行 旧值 + 1等价于 (old, _new) -> old == null ? _new : old + _new,但无需手动判空Integer::sum 是最简合并函数,语义清晰,性能高效合并函数的灵活控制remappingFunction 不仅限于累加,还能按需定制逻辑:
保留最大值:
(old, newVal) -> Math.max(old, newVal)
只保留首次值(不覆盖):
(old, newVal) -> old
拼接字符串:
(old, newVal) -> old == null ? newVal : old + "," + newVal
清空该键:
(old, newVal) -> null(触发 remove)
注意 null 值与线程安全边界merge 行为依赖 Map 实现:HashMap 不是线程安全的,merge 只是逻辑简洁,不保证并发安全ConcurrentHashMap 支持线程安全的 merge,但不允许 key 或 value 为 null(否则抛 NullPointerException)
若业务允许 null 值,建议用 compute() 替代,或提前过滤
