为什么ConcurrentHashMap不允许插null?
创始人
2025-06-29 15:50:53
0

在 Java 语言中,ConcurrentHashMap 和 Hashtable 这些线程安全的集合是不允许 key 或 value 插入 null 值的,而 HashMap 又允许 key 或 value 插入 null 值,这到底是为什么呢?

null 值插入演示

首先给 HashMap 插入 null 值,实现代码如下:

HashMap map = new HashMap();
// 插入 null 值
map.put(null, null);
if (map.containsKey(null)) {
    System.out.println("存在 null");
} else {
    System.out.println("不存在 null");
}

以上程序的执行结果如下:

图片图片

从上述结果可以看出,HashMap 是允许 key 或 value 插入 null 值的。接着我们使用同样的方式尝试给 ConcurrentHashMap 的 key 和 value 插入 null 值,实现代码如下:

图片图片

编译阶段没有报错,执行以上程序,得到的结果如下:

图片图片

从上述报错信息可以看出,使用 ConcurrentHashMap 是不能插入 null 值的,否者程序在运行期间就会报空指针异常。

PS:Hashtable 使用与 ConcurrentHashMap 类似,这里就不再重复演示了。

ConcurrentHashMap 源码分析

为了寻找报错的原因,我们尝试打开 ConcurrentHashMap 的源码一探究竟。打开 ConcurrentHashMap 添加元素的方法 put 实现源码如下:

图片图片

从上述源码可以看出,在添加方法的第一句就加了判断:如果 key 值为 null 或者是 value 值为 null,就直接抛出异常 NullPointerException 空指针异常,这就是咱们前面程序报错的原因了。

探索最终原因

通过上面源码分析,我们似乎已经找到了 ConcurrentHashMap 不允许插入 null 值的原因,用一句话概括就是:乌龟的屁股“规定”!然而,这个原因是不能说服面试官的,虽然源码是这样设计的,但我们要思考的是,这样设计背后更深层次的原因,为什么 ConcurrentHashMap 不允许插入 null?而 HashMap 又允许插入 null 呢?

二义性问题

所谓的二义性问题是指含义不清或不明确。我们假设 ConcurrentHashMap 允许插入 null,那么此时就会有二义性问题,它的二义性含义有两个:

  1. 值没有在集合中,所以返回 null。
  2. 值就是 null,所以返回的就是它原本的 null 值。

可以看出这就是 ConcurrentHashMap 的二义性问题,那为什么 HashMap 就不怕二义性问题呢?

可证伪的 HashMap

上面说到 HashMap 是不怕二义性问题的,为什么呢?这是因为 HashMap 的设计是给单线程使用的,所以如果查询到了 null 值,我们可以通过 hashMap.containsKey(key) 的方法来区分这个 null 值到底是存入的 null?还是压根不存在的 null?这样二义性问题就得到了解决,所以 HashMap 不怕二义性问题。

不可证伪的 ConcurrentHashMap

而 ConcurrentHashMap 就不一样了,因为 ConcurrentHashMap 使用的场景是多线程,所以它的情况更加复杂。我们假设 ConcurrentHashMap 可以存入 null 值,有这样一个场景,现在有一个线程 A 调用了 concurrentHashMap.containsKey(key),我们期望返回的结果是 false,但在我们调用 concurrentHashMap.containsKey(key) 之后,未返回结果之前,线程 B 又调用了 concurrentHashMap.put(key,null) 存入了 null 值,那么线程 A 最终返回的结果就是 true 了,这个结果和我们之前预想的 false 完全不一样。也就是说,多线程的状况非常复杂,我们没办法判断某一个时刻返回的 null 值,到底是值为 null,还是压根就不存在,也就是二义性问题不可被证伪,所以 ConcurrentHashMap 才会在源码中这样设计,直接杜绝 key 或 value 为 null 的歧义问题。

ConcurrentHashMap 设计者的回答

对于 ConcurrentHashMap 不允许插入 null 值的问题,有人问过 ConcurrentHashMap 的作者 Doug Lea,以下是他回复的邮件内容:

The main reason that nulls aren't allowed in ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) is that ambiguities that may be just barely tolerable in non-concurrent maps can't be accommodated. The main one is that if map.get(key) returns null, you can't detect whether the key explicitly maps to null vs the key isn't mapped. In a non-concurrent map, you can check this via map.contains(key),but in a concurrent one, the map might have changed between calls. Further digressing: I personally think that allowing nulls in Maps (also Sets) is an open invitation for programs to contain errors that remain undetected until they break at just the wrong time. (Whether to allow nulls even in non-concurrent Maps/Sets is one of the few design issues surrounding Collections that Josh Bloch and I have long disagreed about.)

It is very difficult to check for null keys and values in my entire application .

Would it be easier to declare somewhere     static final Object NULL = new Object(); and replace all use of nulls in uses of maps with NULL? -Doug

以上信件的主要意思是,Doug Lea 认为这样设计最主要的原因是:不容忍在并发场景下出现歧义!

总结

在 Java 语言中,HashMap 这种单线程下使用的集合是可以设置 null 值的,而并发集合如 ConcurrentHashMap 或 Hashtable 是不允许给 key 或 value 设置 null 值的,这是 JDK 源码层面直接实现的,这样设计的目的主要是为了防止并发场景下的歧义问题。

参考文档

cnblogs.com/fanguangdexiaoyuer/p/12335921.html

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...
《非诚勿扰》红人闫凤娇被曝厕所... 【51CTO.com 综合消息360安全专家提醒说,“闫凤娇”、“非诚勿扰”已经被黑客盯上成为了“木...
2012年第四季度互联网状况报... [[71653]]  北京时间4月25日消息,据国外媒体报道,全球知名的云平台公司Akamai Te...