高德面试:为什么Map不能插入null?
在 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.avacn.site";
ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap();
concurrentHashMap.put(key, null);
System.out.println(concurrentHashMap.get(key));
在运行以上程序时就会报错,如下图所示:

因此,我们可以得出结论:
-
在 HashMap 中,key 和 value 值都可以为 null。
-
在 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 其实有以下两层含义:
-
这个值本身设置的是 null,null 在这里表示的是一种具体的“null”值状态。
-
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。
相关文章:
高德面试:为什么Map不能插入null?
在 Java 中,Map 是属于 java.util 包下的一个接口(interface),所以说“为什么 Map 不能插入 null?”这个问题本身问的不严谨。Map 部分类关系图如下: 所以,这里面试官其实想问的是:为…...
MySQL数据库主从配置
MySQL主从配置 1. 修改数据库my.cnf文件 修改数据库my.cnf文件,在文件中添加如下内容,其中主数据库的server-id必须要比从库的更小。 # 注册集群id server-id101 # 开启二进制日志文件 log-binmysql-bin # 设置日志格式 binlog-formatrow # 开启中继日…...
测试工程师经常使用的Python中的库,以及对应常用的函数
os (操作系统接口) 该库提供了许多与操作系统交互的函数,如文件处理、目录操作、进程管理等。 常用功能包括: os.name: 获取操作系统的名称。 os.path: 用于操作文件路径的模块,如os.path.join拼接路径。 os.mkdir: 创建目录。 os.remove: 删…...
【frp】服务端配置与systemd启动
ini配置的方式已经废弃。官方文档是toml 。阿里云ecs 部署服务端参考大神的文章 使用Frp配置内网访问(穿透) 0.54 版本 我现在用最新的0.58版本。systemd apt install systemdfrp服务端配置 /root/frp目录 vim frps.toml#服务绑定的IP与端口 bindAddr = "0.0.0.0" …...
计算机网络学习实践:模拟RIP动态路由
计算机网络学习实践:模拟RIP动态路由 模拟动态路由RIP协议 1.实验准备 实验环境:华为模拟器ENSP 实验设备: 3个路由器,3个二层交换机(不是三层的),3个PC机 5个网段 192.168.1.0 255.255.…...
详解 Flink 的常见部署方式
一、常见部署模式分类 1. 按是否依赖外部资源调度 1.1 Standalone 模式 独立模式 (Standalone) 是独立运行的,不依赖任何外部的资源管理平台,只需要运行所有 Flink 组件服务 1.2 Yarn 模式 Yarn 模式是指客户端把 Flink 应用提交给 Yarn 的 ResourceMa…...
【UE5.1 角色练习】11-坐骑——Part1(控制大象移动)
前言 在上一篇(【UE5.1 角色练习】10-物体抬升、抛出技能 - part2)基础上创建一个新的大象坐骑角色,并实现控制该角色行走的功能。 效果 步骤 1. 在商城中下载“African Animal Pack”资产和“ANIMAL VARIETY PACK”资产导入工程中 2. 复…...
数据结构严蔚敏版精简版-线性表以及c语言代码实现
线性表、栈、队列、串和数组都属于线性结构。线性结构的基本特点是除第一个元素无直接前驱,最后一个元素无直接后继之外,其他每个数据元素都有一个前驱和后继。 1 线性表的定义和特点 如此类由n(n大于等于0)个数据特性相同的元素…...
【react】react项目支持鼠标拖拽的边框改变元素宽度的组件
目录 安装使用方法示例Props 属性方法示例代码调整兄弟div的宽度 re-resizable github地址 安装 $ npm install --save re-resizable这将安装re-resizable库并将其保存为项目的依赖项。 使用方法 re-resizable 提供了一个 <Resizable> 组件,它可以包裹任何…...
QT 创建文件 Ui 不允许使用不完整类型,可以尝试添加一下任何头文件
#include "debug.h" #include "qmessagebox.h" #pragma execution_character_set("utf-8") //QT 创建文件 Ui 不允许使用不完整类型,尝试添加一下任何头文件,或者添加ui_xx.h头文件 debug::debug(QWidget *parent) : QDialog(p…...
Python:深入探索其生态系统与应用领域
Python:深入探索其生态系统与应用领域 Python,作为一种广泛应用的编程语言,其生态系统之丰富、应用领域之广泛,常常令人叹为观止。那么,Python究竟涉及哪些系统?本文将从四个方面、五个方面、六个方面和七…...
EXCEL从图片链接获取图片
step1: 选中图片地址列 step2:开发工具→Visual Basic 文件→导入 导入我制作的脚本(代码见文章末尾) 点击excel的小图标回到表格界面。 点击【宏】 选中刚才导入的脚本,点执行,等待完成。 代码本体: Sub InsertPict…...
Docker迁移默认存储目录(GPT-4o)
Docker在Ubuntu的默认存储目录是/var/lib/docker,要将 Docker 的默认存储目录迁移到指定目录(譬如大存储磁盘),可以通过修改 Docker 守护进程的配置文件来实现。 1.创建新的存储目录: 选择你想要存储 Docker 分层存储…...
植物大战僵尸杂交版2.0.88最新版安装包
游戏简介 游戏中独特的杂交植物更是为游戏增添了不少亮点。这些杂交植物不仅外观独特,而且拥有更强大的能力,能够帮助玩家更好地应对游戏中的挑战。玩家可以通过一定的条件和方式,解锁并培养这些杂交植物,从而不断提升自己的战斗…...
MQ基础(RabbitMQ)
通信 同步通信:就相当于打电话,双方交互是实时的。同一时刻,只能与一人交互。 异步通信:就相当于发短信,双方交互不是实时的。不需要立刻回应对方,可以多线程操作,跟不同人同时聊天。 RabbitM…...
eclipse添加maven插件
打开eclipse菜单 Help/Install New SoftwareWork with下拉菜单选择 2022-03 - https://download.eclipse.org/releases/2022-03‘type filter text’搜索框中输入 maven选择 M2E - Maven Integration for Eclipse一路next安装,重启eclipseImport项目时,就…...
知识库系统:从认识到搭建
在这个信息过载的时代,企业越来越需要一个集中的知识库系统来促进员工协作和解决问题。本文跟着LookLook同学一起来探讨搭建高效知识库系统的所有注意事项和知识库系统的最佳推荐。 | 什么是知识库系统 知识库系统是一种软件或工具,旨在填补组织内的知识…...
JVM双亲委派模型
在之前的JVM类加载器篇中说过,各个类加载器都有自己加载的范围,比如引导类加载器只加载Java核心库中的class如String,那如果用户自己建一个包名和类名与String相同的类,会不会被引导类加载器加载。可以通过如下代码测试࿰…...
Python语言与算法:深度探索与实战应用
Python语言与算法:深度探索与实战应用 在数字化浪潮汹涌的时代,Python语言以其简洁、易读和强大的功能库成为了编程界的翘楚。而算法,作为计算机科学的核心,是解决问题、优化性能的关键。本文将围绕Python语言与算法的结合&#…...
Python实现连连看7
3.3 根据地图显示图片 在获取了图片地图之后,就可以根据该图片地图显示图片了。显示图片的功能在自定义函数drawMap()中实现。 3.3.1 清除画布中的内容 在画布上显示图片之前,需要将画布中图1的启动界面内容清除,代码如下所示。 canvas.delete(all) 其中,delete()方法…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
