面试突击19:为什么ConcurrentHashMap不允许插入null值?

哈喽,大家好,今天来盘《Java面试突击》系列。

ConcurrentHashMap为什么不允许插入null值?

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

null值插入演示

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

面试突击19:为什么ConcurrentHashMap不允许插入null值?

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

面试突击19:为什么ConcurrentHashMap不允许插入null值?

从上述结果可以看出,HashMap是允许key或value插入null值的。

接着我们使用同样的方式尝试给ConcurrentHashMap的key和value插入null值,实现代码如下:

面试突击19:为什么ConcurrentHashMap不允许插入null值?

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

面试突击19:为什么ConcurrentHashMap不允许插入null值?

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

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

ConcurrentHashMap源码分析

为了寻找报错的原因,我们尝试打开ConcurrentHashMap的源码一探究竟。

打开ConcurrentHashMap添加元素的方法put实现源码如下:

面试突击19:为什么ConcurrentHashMap不允许插入null值?

从上述源码可以看出,在添加方法的第一句就加了判断:如果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的作者DougLea,以下是他回复的邮件内容:

面试突击19:为什么ConcurrentHashMap不允许插入null值?

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

总结

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

好了,本期内容到这里就结束了。


是非审之于己,毁誉听之于人,得失安之于数。

公众号:Java面试真题解析

面试合集:gitee.com/mydb/interview

上一篇:使用 nice、cpulimit 和 cgroups 限制 cpu 占用率


下一篇:开源Math.NET基础数学类库使用(05)C#解析Delimited Formats数据格式