在 Java 中,Map 是属于 java.util 包下的一个接口(interface),所以说“为什么 Map 不能插入 null?”这个问题本身问的不严谨。Map 部分类关系图如下:



所以,这里面试官其实想问的是:为什么 ConcurrentHashMap 不能插入 null?

1.HashMap和ConcurrentHashMap的区别

HashMap 和 ConcurrentHashMap 在对待 null 的态度上是不同的,在 Java 中,HashMap 是允许 key 和 value 值都为 null 的,如下代码所示:

HashMap<String, Object> map = new HashMap();
map.put(null, null);
if (map.containsKey(null)) {
System.out.println("存在 null");
} else {
System.out.println("不存在 null");
}

以上程序的执行结果如下:

存在 null

从上述结果可以看出,HashMap 是允许 key 和 value 值都为 null 的。

但 ConcurrentHashMap 就不同了,它不但 key 不能为 null,而且 value 也不能为 null,如以下代码所示:

ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap();
concurrentHashMap.put(null, "javacn.site");
System.out.println(concurrentHashMap.get(null));

在运行以上程序时就会报错,如下图所示:



当然,当你为 ConcurrentHashMap 的 value 值设置 null 时也会报错,如下代码所示:

String key = "www.Javacn.site";
ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap();
concurrentHashMap.put(key, null);
System.out.println(concurrentHashMap.get(key));

在运行以上程序时就会报错,如下图所示:



因此,我们可以得出结论:

  1. 在 HashMap 中,key 和 value 值都可以为 null。
  2. 在 ConcurrentHashMap 中,key 或者是 value 值都不能为 null。

2.为什么不能插入null?

如果我们查看 ConcurrentHashMap 的源码,就能发现为什么 ConcurrentHashMap 不能插入 null 了,以下是 ConcurrentHashMap 添加元素时的部分核心源码:

// 添加 key 和 value
public V put(K key, V value) {
return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
// 如果 key 或 value 为 null 的话直接抛出空指针异常
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
// 忽略其他代码......
}

从上述 ConcurrentHashMap 添加元素的第一行源码就可以看出,当 key 或 value 为 null 时,会直接抛出空指针异常,这就是 ConcurrentHashMap 之所以不能插入 null 的根本原因了,因为源码就是这样设计的。

3.更深层次的原因

那么问题来了,为什么 ConcurrentHashMap 的实现源码中,不允许为 key 或者是 value 设置 null 呢?

这就要从 ConcurrentHashMap 的使用场景说起了,在 Java 中,ConcurrentHashMap 是用于并发环境中执行的线程安全的容器,而 HashMap 是用于单线程环境下执行的非线程安全的容器,而并发环境下的运行更复杂,如果我们允许 ConcurrentHashMap 的 key 或者是 value 为 null 的情况下,就会存在经典的“二义性问题”

3.1 什么是二义性问题?

所谓的二义性问题指的是代码或表达式存在多种理解或解释,导致程序的含义不明确或模糊。

以 ConcurrentHashMap 不允许为 null 的二义性问题来说,null 其实有以下两层含义:

  1. 这个值本身设置的是 null,null 在这里表示的是一种具体的“null”值状态。
  2. null 还表示“没有”的意思,因为没有设置,所以啥也没有。

所以,如果 ConcurrentHashMap 允许插入 null 值,那么就会存在二义性问题。

那就有同学会问了,为什么 HashMap 允许插入 null,它就不怕有二义性问题吗?

3.1 可证伪的HashMap

HashMap 之所以不怕二义性问题的原因是,HashMap 的设计是给单线程使用的,而单线程下的二义性问题是能被证明真伪的,所以也就不存在二义性问题了(能被证明的问题就不是二义性问题)

例如,当我们给 HashMap 的 key 设置为 null 时,我们可以通过 hashMap.containsKey(key) 的方法来区分这个 null 值到底是存入的 null?还是压根不存在的 null?这样二义性问题就得到了解决,所以 HashMap 的二义性问题可被证明真伪,所以就不怕二义性问题,因此也就可以给 key 或者 value 设置 null 了。

3.2 不可证伪的ConcurrentHashMap

而 ConcurrentHashMap 就不一样了,因为 ConcurrentHashMap 是设计在多线程下使用的,而多线程下的二义性问题是不能被证明真伪的,所以二义性问题是真实存在的

因为在你在证明二义性问题的同时,可能会有另一个线程影响你的执行结果,所以它的二义性问题就一直存在。

例如,当 ConcurrentHashMap 未设置 key 为 null 时,会有这样一个场景,当一个线程 A 调用了 concurrentHashMap.containsKey(key),我们期望返回的结果是 false,但在我们调用 concurrentHashMap.containsKey(key) 之后,未返回结果之前,线程 B 又调用了 concurrentHashMap.put(key,null) 存入了 null 值,那么线程 A 最终返回的结果就是 true 了,这个结果和我们之前预想的 false 完全不一样,这就是不能被证伪的二义性问题。

所以说,多线程的执行比较复杂,在多线程下 null 的二义性问题是不能被证明真伪的(因为在一个线程执行验证时,可能会有另一个线程改动结果,造成结果不准确),所以 ConcurrentHashMap 为了避免这个二义性问题,所以就在源码中禁用了 null 值作为 key 或 value。

课后思考

除了 ConcurrentHashMap 之后,还有哪些容器不允许使用 null 作为 key 或者 value 呢?

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

高德面试:为什么Map不能插入null?的更多相关文章

  1. 面试题:对NotNull字段插入Null值 有啥现象?

    Hi,大家好!我是白日梦. 今天我要跟你分享的话题是:"对NotNull字段插入Null值有啥现象?" 一. 推荐阅读 首发地址:https://mp.weixin.qq.com/ ...

  2. MySQL timestamp NOT NULL插入NULL的问题

    explicit_defaults_for_timestamp MySQL 5.6版本引入 explicit_defaults_for_timestamp 来控制对timestamp NULL值的处理 ...

  3. Ibatis insert语句插入null引发的错误

    公司使用的orm框架为ibatis,其中默认的insert语句一直都是这样写的: <insert id="insert" parameterClass="activ ...

  4. c/c++ 标准库 map set 插入

    标准库 map set 插入 一,插入操作 有map如下: map<string, size_t> cnt; 插入方法: 插入操作种类 功能描述 cnt.insert({"abc ...

  5. FDQuery 怎么能插入NULL参数

    [FireDAC][Phys][MSSQL]-335. Parameter [fieldAA] data type is unknown. Hint: specify TFDParam.DataTyp ...

  6. 小白鼠排队(map容器插入数据的四种方法)

    题目描述 N只小白鼠(1 <= N <= 100),每只鼠头上戴着一顶有颜色的帽子.现在称出每只白鼠的重量,要求按照白鼠重量从大到小的顺序输出它们头上帽子的颜色.帽子的颜色用“red”,“ ...

  7. C++(十一)— map的插入、查找、删除

    标准库map类型是一种以键-值(key-value)存储的数据类型. map是STL的一个关联容器.它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值 ...

  8. automapper如何全局配置map条件过滤null值空值对所有映射起效

    原文 automapper如何全局配置map条件过滤null值空值对所有映射起效 我们在使用automapper的时候经常会遇到这样的问题:假设展示给用户的数据我们用UserDto类,User类就是我 ...

  9. kettle 数据抽取时会出现 无法插入NULL

    kettle 数据抽取时会出现 无法插入NULL,其实是空字符串,原因是kettle默认不区分空字符串和NULL. 解决办法: 修改kettle.properties 文件:

  10. Jackson中处理map中的null key 或者null value 及实体字段中的null value

    1.map中有null key时的序列化  当有null key时,jackson序列化会报 Null key for a Map not allowed in JSON (use a convert ...

随机推荐

  1. async与await暂停作用

    1. async function Request () { await new Promise(res => { setTimeout(() => { console.log(1) re ...

  2. 力扣1068(MySQL)-产品销售分析Ⅰ(简单)

    题目: 销售表 Sales: 产品表 Product: 写一条SQL 查询语句获取 Sales 表中所有产品对应的 产品名称 product_name 以及该产品的所有 售卖年份 year 和 价格 ...

  3. Pygame安装以及解决问题:Try to run this command from the system terminal. Make sure that you use the correct version of 'pip......

    在这里记录一下我的安装过程: 1.首先找到自己python程序安装目录下的Scripts文件夹(里面有pip这里面): 2.使用快捷键win + R 打开终端,先进入到安装python的盘符,然后进入 ...

  4. PolarDB-X 源码解读系列:DML 之 INSERT IGNORE 流程

    简介: 本文将进一步介绍 PolarDB-X 中 INSERT IGNORE 的执行流程,其根据插入的表是否有 GSI 也有所变化. 作者:潜璟 在上一篇源码阅读中,我们介绍了 INSERT 的执行流 ...

  5. 工商银行分布式服务C10K场景的解决方案

    简介: 未来,中国工商银行将持续致力于 Dubbo 的金融级规模化应用. 作者:颜高飞,微服务领域架构师,主要从事服务发现.高性能网络通信等研发工作,擅长 ZooKeeper.Dubbo.RPC 协议 ...

  6. 实时数仓入门训练营:Hologres性能调优实践

    简介: <实时数仓入门训练营>由阿里云研究员王峰.阿里云资深技术专家金晓军.阿里云高级产品专家刘一鸣等实时计算 Flink 版和 Hologres 的多名技术/产品一线专家齐上阵,合力搭建 ...

  7. [Mobi] frida Hook 略知一二: frida-CLI, frida-server

    Frida 是一款基于 python + javascript 的 hook 框架,主流平台都支持,由于是基于脚本的交互,因此相比 xposed 和 substrace cydia 更加便捷. 使用时 ...

  8. WPF 使用 ManipulationDemo 工具辅助调试设备触摸失效问题

    本文将和大家介绍我所在的团队开源的 ManipulationDemo 工具.通过 ManipulationDemo 工具可以提升调试设备触摸失效的效率 此工具在 GitHub 上完全开源,请看 htt ...

  9. 2019-9-30-dotnet-枚举当前设备wifi热点

    title author date CreateTime categories dotnet 枚举当前设备wifi热点 lindexi 2019-09-30 14:42:18 +0800 2019-9 ...

  10. HttpClient配置SSL绕过https证书以及双向认证

    HttpClient简介 1.HTTP 协议是 Internet 上使用得最多.最重要的协议之一,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源.虽然在 JDK 的 java ...