关于后端:面试突击19为什么ConcurrentHashMap不允许插入null值

5次阅读

共计 2993 个字符,预计需要花费 8 分钟才能阅读完成。

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

null 值插入演示

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

HashMap<String, Object> 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 源码层面间接实现的,这样设计的目标次要是为了避免并发场景下的歧义问题。

参考文档

www.cnblogs.com/fanguangdexiaoyuer/p/12335921.html

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

公众号:Java 面试真题解析

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

正文完
 0