作者:磊哥

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

以上程序的执行结果如下:从上述结果可以看出,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面试真题解析

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

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

为什么ConcurrentHashMap不允许插入null值?相关推荐

  1. mysql添加列默认null_MySQL-无法在列中插入NULL值,但是我指定了默认值吗?

    我在MySQL中有一个表,其中有几列具有指定的默认值,但是当我尝试插入行时(未为这些默认列指定值),它抛出一个错误,提示我无法插入NULL值. 这是表格示例: CREATE TABLE `users` ...

  2. mybatis入门学习(九) -DB环境切换、使用注解、事务提交、获取自增ID、多参数传参、鉴别器、内置参数、批量写入、Oracle字段无法插入 null 值

    一.多数据库环境切换 1.config配置: <!-- default="mydemo" 指定连接的数据库 --><environments default=&q ...

  3. MySQL8.0向NOT NULL的TIMESTAMP类型字段插入NULL值的问题

    在学习SSM框架的过程中需实现如下过程: Key1:需要通过mybatis向数据库插入一条记录,且此记录包含timestamp类型字段.假设字段为[id,name,tel,pay_time],第四个为 ...

  4. kettle的乱码和null值无法插入

    今天再弄kettle 的时候遇到两个问题, 1.更新数据的时候,遇到一个问题,就是我的输入表主键不是null,但是他一直说我插入null值 2.更新的数据,出现乱码 首先说第一个 其实kettle在进 ...

  5. golang 数据库null值错误 解决方法

    遇到问题: converting NULL to string is unsupported 定义的结构体中 结构体成员类型为string,从mysql数据库中查询数据中有NULL值,go语言执行sc ...

  6. mysql insert 自增_MySQL自增列插入0值的解决方案

    在将数据库从MSSQL迁移到MySQL的过程中,基于业务逻辑的要求,需要在MySQL的自增列插入0值.在MSSQL中是这样完成的: string sql;sql = " set identi ...

  7. 你还不知道mysql中空值和null值的区别吗?

    前言 最近发现带的小伙伴写sql对于空值的判断方法不正确,导致程序里面的数据产生错误,在此进行一下整理,方便大家以后正确的判断空值.以下带来示例给大家进行讲解. 建表 create table tes ...

  8. MySQL从入门到精通50讲(十)-MySQL中null值如何处理

    MySQL NULL 值处理 我们已经知道MySQL使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作. 为了 ...

  9. mysql timestamp 插入null报错_读MySQL 5.7文档11.2 Date and Time Data Types总结

    作者:魏新平,知数堂第5期MySQL实战班学员,第10期MySQL优化班学员,现任职助教. 读MySQL5.7文档11.2 Date and Time Data Types MySQL的时间类型分为D ...

最新文章

  1. 使用git进行源代码管理
  2. Redis序列化配置类
  3. 挤牙膏只服英特尔!新酷睿14nm+++,性能竟超过AMD 7nm?
  4. 什么是CommonJS?
  5. 类库 通用变量 is和as 委托
  6. 17年前存款11万,取款时银行以销户为由拒绝,法院判银行全额赔付
  7. 5120v2怎么配置web登陆_阿里企业邮箱如何配置和添加到第三个电子邮件客户端中?...
  8. VSRE的完整形式是什么?
  9. 网卡流量统计实用工具nicstat
  10. Day4 MySql触发器视图索引以及设计优化
  11. android 静态编译链接,Android NDK:使用预编译的静态库链接
  12. 计算机2020中级考试题,突发!2020年中级考试分值及评分标准大变!
  13. Springboot接收ajax提交JSON数组
  14. MED-V实战之镜像测试,MED-V系列之五
  15. vb6与access数据库交互常见问题——未完待续
  16. vue 电子签名插件
  17. TODA项目Part1—后端项目设置与连接数据库
  18. 自比毕加索,揭秘神秘的“苹果大学”
  19. 使用谷歌浏览器模拟发送http请求
  20. shell基础---exit用法

热门文章

  1. 局域网内文件传输速度_详解蒲公英路由器组网 实现文件共享
  2. Ubuntu下面apache2安装
  3. SmoothNLP 中文NLP文本处理工具 Python 实战示范
  4. pip模块 redis、xlrd、xlutils、nnlog、requests
  5. Epson C1100报错“Service Req E511”的处理方法
  6. Android Studio导入工程的正确姿势
  7. cocos2d-x lua 学习笔记(1) -- 环境搭建
  8. 如何查看linux的版本
  9. u boot 驱动完成
  10. 一年Java经验应该会些什么