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

f4e7851b3bb1d59b9704b4622e8dc7e0.png

作者:磊哥

来源 | Java面试真题解析(ID:aimianshi666)

转载请联系授权(微信ID:GG_Stone)

在 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");
}

以上程序的执行结果如下:726ea6156026951080f2e1baf874eeac.png从上述结果可以看出,HashMap 是允许 key 或 value 插入 null 值的。接着我们使用同样的方式尝试给 ConcurrentHashMap 的 key 和 value 插入 null 值,实现代码如下:44d6c96a8843a53798c4bc7d22256457.png编译阶段没有报错,执行以上程序,得到的结果如下:9f83dfd98dff1318bcb077368bd9bbfd.png从上述报错信息可以看出,使用 ConcurrentHashMap 是不能插入 null 值的,否者程序在运行期间就会报空指针异常。

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

ConcurrentHashMap 源码分析

为了寻找报错的原因,我们尝试打开 ConcurrentHashMap 的源码一探究竟。打开 ConcurrentHashMap 添加元素的方法 put 实现源码如下:4297e5fc093edc8d2361254a55964302.png从上述源码可以看出,在添加方法的第一句就加了判断:如果 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面试真题解析

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

ab2f46935149539f42b1d0c647532f52.gif

往期推荐
面试突击18:为什么ConcurrentHashMap是线程安全的?面试突击17:HashMap除了死循环还有什么问题?面试突击16:为什么HashMap会产生死循环?求点赞、在看、分享三连

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544318.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET APlayer播放器 demo

工作需要,想开发一款播放器,当无意间浏览到APlayer的时候大爱啊,有木有迅速投入精力,在APlayer论坛看大牛们的作品及经验,看SDK中提供的chm电子书最后看了博客园中周见智的文章(灰常好!最终作品也用的他的demo改进)先来晒一下最终的效果图片。 效果截图&#xff1a;1.初始界面。…

Spring Cloud Alibaba Nacos 服务注册与发现功能实现!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;Nacos 是 Spring Cloud Alibaba 中一个重要的组成部分&#xff0c;它提供了两个重要的功能&#xff1a;服务注册与发现和统一…

加分进了字节,MySQL真yyds!

Java研发工程师必备技能非MySQL莫属&#xff0c;虽说易学好上手&#xff0c;但应对大厂面试&#xff0c;最容易遭遇滑铁卢、功败垂成的也是它。上手简单&#xff0c;玩转难&#xff0c;才是这款开源数据库叱咤业界多年的真实写照。MySQL 8.0正式版的到来&#xff0c;在性能和速…

Nacos服务注册与发现的2种实现方法!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;Spring Cloud Alibaba 技术体系中的 Nacos&#xff0c;提供了两个重要的功能&#xff1a;注册中心&#xff08;服务注册与发…

面试必备:Spring 面试 63 问!

作者 | 夏目blog.csdn.net/wuzhiwei549/article/details/122324261Sping原理Spring是一个轻量级Java开发框架&#xff0c;最早有Rod Johnson创建&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack&#xff0…

为什么HashMap会产生死循环?

作者&#xff1a;磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;面试合集&#xff1a;https://gitee.com/mydb/interviewHashMap 死循环是一个比较常见、比较经典的问题&am…

iOS的自动化测试

2019独角兽企业重金招聘Python工程师标准>>> iOS的自动化测试:http://www.360doc.com/content/13/1225/22/1912775_340124906.shtml 转载于:https://my.oschina.net/CeShiXiaoSongShu/blog/496660

为什么阿里全面推动 K8S 落地,咬紧牙关也要搞云原生?

身为让容器应用实现大规模工业生产的一大功臣&#xff0c;过去几年&#xff0c;Kubernetes 势头迅猛&#xff0c;BAT、京东、美团、字节都走上了全域容器化部署以及云原生架构的康庄大道。而作为支撑阿里万亿级应用背后的核心&#xff0c;阿里云早在2016年就顺势搭上容器化这趟…

Spring Cloud Alibaba Nacos路由策略之保护阈值!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在 Nacos 的路由策略中有 3 个比较重要的内容&#xff1a;权重、保护阈值和就近访问。因为这 3 个内容都是彼此独立的&#…

浅谈 OneAPM 在 express 项目中的实践

【编者按】OneAPM 运营团队&#xff0c;近日在 github 上发现了一篇文章&#xff0c;特别奉献给大家。本文作者王宇先生从2015年年初就开始使用我们的产品&#xff0c;也是OneAPM 的忠实用户。 OneAPM 是一个优秀的性能监控平台。为什么我们要使用性能监控呢&#xff1f; 并不是…

【万字长文】Spring Cloud Alibaba 开箱即用!

互联网时代&#xff0c;面对复杂业务&#xff0c;讲究 分而治之。将一个大的单体系统拆分为若干个微服务&#xff0c;保证每个系统的职责单一&#xff0c;可以垂直深度扩展。但是一个个独立的微服务像一座座孤岛&#xff0c;如何将他们串联起来&#xff0c;才能发挥最大价值。这…

HDFS DataNode 设计实现解析

前文分析了 NameNode&#xff0c;本文进一步解析 DataNode 的设计和实现要点。 文件存储 DataNode 正如其名是负责存储文件数据的节点。HDFS 中文件的存储方式是将文件按块&#xff08;block&#xff09;切分&#xff0c;默认一个 block 64MB&#xff08;该大小可配置&#xff…

芭比扣了!Nacos中服务删除不了,肿么办?

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;前两天遇到了一个问题&#xff0c;Nacos 中的永久服务删除不了&#xff0c;折腾了一番&#xff0c;最后还是顺利解决了。以下…

Spring Cloud OpenFeign夺命连环9问,这谁受得了?

1、前言前面介绍了Spring Cloud 中的灵魂摆渡者Nacos&#xff0c;和它的前辈们相比不仅仅功能强大&#xff0c;而且部署非常简单。今天介绍一款服务调用的组件&#xff1a;OpenFeign&#xff0c;同样是一款超越先辈&#xff08;Ribbon、Feign&#xff09;的狠角色。文章目录如下…

玩转Nacos参数配置!多图勿点

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;Nacos 中的参数有很多&#xff0c;如&#xff1a;命名空间、分组名、服务名、保护阈值、服务路由类型、临时实例等&#xff…

为什么wait/notify必须要和synchronized一起使用?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在多线程编程中&#xff0c;wait 方法是让当前线程进入休眠状态&#xff0c;直到另一个线程调用了 notify 或 notify…

Magento Add Fee or Discount to Order Totals

2019独角兽企业重金招聘Python工程师标准>>> In this tutorial, we will see how to add new line item to magento order totals. What this means is that, how to add an additional Fee or Discount, or any kind of charge to order total of the magento chec…

再见 Feign!推荐一款微服务间调用神器,跟 SpringCloud 绝配!

在微服务项目中&#xff0c;如果我们想实现服务间调用&#xff0c;一般会选择Feign。之前介绍过一款HTTP客户端工具Retrofit&#xff0c;配合SpringBoot非常好用&#xff01;其实Retrofit不仅支持普通的HTTP调用&#xff0c;还能支持微服务间的调用&#xff0c;负载均衡和熔断限…

Spring Cloud Alibaba Nacos 的 2 种健康检查机制!

作者 | 磊哥来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;Spring Cloud Alibaba Nacos 作为注册中心不止提供了服务注册和服务发现功能&#xff0c;它还提供了服务可用性监测的机制。…

Python之包管理工具

在Python环境中已经有很多成熟的包&#xff0c;可以通过安装这些包来扩展我们的程序。 例如&#xff0c;很多时候Python开发人员都会去PyPI网站去查找自己想要使用的包&#xff0c;然后进行安装。PyPI &#xff08; Python Package Index&#xff09;是获得第三方 Python 软件包…