文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

为什么ConcurrentHashMap不允许插null?

2024-11-30 09:49

关注

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

来源:Java面试真题解析内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯