bigywater.tech
Java 内部原理 · 113

Map.merge() 真实验证:BiFunction返回null,真实会把这个key从map里删除

merge()是Java8引入的最实用的Map方法之一,一行代码搞定词频统计——这篇真实验证它的基础用法,以及一个容易被文档一带而过、但真实很有用的行为:BiFunction返回null会发生什么。

key不存在时真实不调用BiFunction
真实验证:merge()发现key不存在时,真实直接使用传入的value,完全不会触发BiFunction回调
返回null真实删除整个key
真实验证:BiFunction返回null后,这个key真实从map里彻底消失了,不是存入了一个value为null的条目

上一篇:Map.computeIfAbsent() 真实验证

真实验证:merge()一行代码实现词频统计

merge()最经典的应用场景,对比传统写法真实简洁很多。

MapMergeDemo.java Map<String, Integer> wordCount = new HashMap<>(); for (String w : words) { wordCount.merge(w, 1, Integer::sum); }
真实实测
真实词频统计结果: {a=3, b=2, c=1}

真实结果验证了 merge(key, value, remappingFunction) 的核心语义:如果 key 不存在,直接存入 value;如果存在,用 remappingFunction 把旧值和新值合并成一个结果重新存入。一行 merge(w, 1, Integer::sum) 真实同时覆盖了"首次出现"和"再次出现累加"两种场景,不需要像老式写法那样先 containsKey() 判断再分支处理。

真实验证:key不存在时,BiFunction真实完全不会被调用

真实实测
key不存在时merge()真实结果: {x=100}

真实结果证明当 "x" 这个key首次出现时,merge() 内部真实直接把传入的 value(100)存进去,完全不会调用 BiFunction——在这段测试代码里,如果 BiFunction 真的被调用了会打印一行提示日志,但真实运行结果里这行日志没有出现,证实了这个短路行为。

真实验证:BiFunction返回null,真实把key彻底删除

这是merge()最容易被忽视、但真实很有用的一个行为——当合并函数返回null,会发生什么?

MapMergeDemo.java map2.put("y", 5); map2.merge("y", 3, (oldV, newV) -> null); // 故意返回null
真实实测
BiFunction返回null后,key真实还在map里吗: false,真实map内容: {}
真实的删除机制,不是存入null。这个结果证明 merge() 内部真实做了检查:如果 remappingFunction 的返回值是 null,它不会把这个 null 当作一个合法的值存进map(那样会变成一个"key存在但value是null"的条目),而是真实调用了内部的删除逻辑,把这个key连同它原来的值一起从map里彻底移除。这条设计规则在 compute()、computeIfPresent() 上同样适用,是JDK有意为之的统一约定:把"返回null"作为表达"这个映射不应该存在了"的语义信号。

真实应用:用这个特性实现"库存归零自动移除"

MapMergeDemo.java inventory.merge("item1", -3, (oldV, newV) -> { int result = oldV + newV; return result <= 0 ? null : result; // 库存归零就删除这个条目 });
真实实测
库存扣减到0后,item1真实还在inventory里吗: false

真实结果验证了这个删除机制在实际业务场景里的用法:一次 merge() 调用真实同时完成了"扣减数量"和"数量归零就自动清理这个条目"两件事,不需要在业务代码里额外写一个判断和 remove() 调用。

真实验证:merge()对null value参数真实抛异常

真实实测
merge()传入null作为value真实抛出 NullPointerException

真实结果证明 merge() 的第二个参数(初始value)本身不允许是 null——这和"回调函数可以返回null来触发删除"是两个不同层面的规则:传入的初始值必须是真实有效的值,但计算过程中的中间结果可以用 null 表示"应该删除"。

分步演示:merge() 的三种真实分支

每一步的 map 内容都是真实运行这段代码之后的状态,包含词频累加、key 不存在时的短路、以及 BiFunction 返回 null 时的删除。

六次 merge() 建出词频统计,再看 key 不存在、BiFunction 返回 null 这两种真实分支
这一步发生变化的 key

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,词频统计和null删除行为重复运行完全一致。
    • 和Map.computeIfAbsent()那篇一起看,能更完整理解 Java 8 引入的这一族Map方法(compute/computeIfAbsent/computeIfPresent/merge)在null值处理和重入限制上共享的统一设计规则。
    • 没有涉及:ConcurrentHashMap.merge()在高并发场景下的真实原子性保证、merge()和putIfAbsent()+replace()手动组合实现相同逻辑时的真实原子性差异——这两个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号