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

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

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

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

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

null值插入演示

首先给HashMap插入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的作者DougLea,以下是他回复的邮件内容:

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

总结

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

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


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

公众号:Java面试真题解析

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

相关文章
|
11月前
|
存储 开发者
HashMap和Hashtable的key和value可以为null吗,ConcurrentHashMap呢
HashMap的key可以为null,value也可以为null;Hashtable的key不允许为null,value也不能为null;ConcurrentHashMap的key不允许为null
|
缓存 安全 算法
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
Java面试题:如何通过JVM参数调整GC行为以优化应用性能?如何使用synchronized和volatile关键字解决并发问题?如何使用ConcurrentHashMap实现线程安全的缓存?
148 0
|
10月前
|
存储 缓存 安全
大厂面试高频:ConcurrentHashMap 的实现原理( 超详细 )
本文详细解析ConcurrentHashMap的实现原理,大厂高频面试,必知必备。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:ConcurrentHashMap 的实现原理( 超详细 )
|
存储 安全 Java
Java集合类面试十七】、介绍一下ConcurrentHashMap是怎么实现的?
ConcurrentHashMap在JDK 1.7中通过分段锁实现线程安全,在JDK 1.8中则采用Node数组配合链表和红黑树,并使用Synchronized和CAS操作提高并发性能。
Java集合类面试十七】、介绍一下ConcurrentHashMap是怎么实现的?
|
算法 Java
【Java集合类面试十八】、ConcurrentHashMap是怎么分段分组的?
ConcurrentHashMap通过分段锁(Segment)实现高效并发访问,get操作无需加锁,而put操作首先判断是否需要扩容,然后通过两次hash定位并尝试使用CAS和锁机制安全地添加元素。
|
安全 Java
【Java集合类面试十六】、HashMap与ConcurrentHashMap有什么区别?
HashMap是非线程安全的,而ConcurrentHashMap通过减少锁粒度来提高并发性能,检索操作无需锁,从而提供更好的线程安全性和性能。
|
安全 算法 Java
Java面试题:如何使用并发集合,例如ConcurrentHashMap?
Java面试题:如何使用并发集合,例如ConcurrentHashMap?
93 1
|
消息中间件 安全 Java
高德面试:为什么Map不能插入null?
在 Java 中,Map 是属于 java.util 包下的一个接口(interface),所以说“为什么 Map 不能插入 null?”这个问题本身问的不严谨。Map 部分类关系图如下: ![image.png](https://cdn.nlark.com/yuque/0/2024/png/92791/1717578225132-241fa833-180d-41ab-95eb-3d7cd1118633.png#averageHue=%233f3424&clientId=u395c5adf-4577-4&from=paste&height=687&id=u98d6a0bc&originHeig
125 1
|
设计模式 并行计算 安全
Java面试题: 如何使用装饰器模式来增强ConcurrentHashMap的功能?在什么情况下应该使用CopyOnWriteArrayList而不是ArrayList?
Java面试题: 如何使用装饰器模式来增强ConcurrentHashMap的功能?在什么情况下应该使用CopyOnWriteArrayList而不是ArrayList?
82 0
|
存储 安全 Java
Java面试题:请解释Java内存模型,并说明如何在多线程环境下使用synchronized关键字实现同步,阐述ConcurrentHashMap与HashMap的区别,以及它如何在并发环境中提高性能
Java面试题:请解释Java内存模型,并说明如何在多线程环境下使用synchronized关键字实现同步,阐述ConcurrentHashMap与HashMap的区别,以及它如何在并发环境中提高性能
121 0