为什么HashMap的键值可以为null,而ConcurrentHashMap不行?

写在开头

今天在写《HashMap很美好,但线程不安全怎么办?ConcurrentHashMap告诉你答案!》这篇文章的时候,漏了一个知识点,知道晚上吃饭的时候才凸显想到,关于ConcurrentHashMap在存储Key与Value的时候,是否可以存null的问题,按理说这是一个小问题,但build哥却不敢忽视,尤其在现在很多面试官都极具挑剔的环境下,万一同学们刷到了咱的博客,回答中遗漏了这个小细节,错过了面试官的考验,那咱可就成罪人了。
接下来我们就将HashMap、Hashtable、ConcurrentHashMap这三集合类的键值是否可以null的问题,放一起对比去学习一下。

Hashtable的键值与null

虽然我们在讲解HashMap与Hashtable作对比时,已经说了Hashtable在存储key与value时均不可为null,但当时的侧重点全在HashMap身上,就没有详细的解释原因,下面我们跟进put源码中去一探缘由。

【源码解析1】

public synchronized V put(K key, V value) {// 确认值不为空if (value == null) {throw new NullPointerException(); // 如果值为null,则抛出空指针异常}// 确认值之前不存在Hashtable里Entry<?,?> tab[] = table;int hash = key.hashCode(); // 如果key如果为null,调用这个方法会抛出空指针异常int index = (hash & 0x7FFFFFFF) % tab.length;//计算存储位置//遍历,看是否键或值对是否已经存在,如果已经存在返回旧值@SuppressWarnings("unchecked")Entry<K,V> entry = (Entry<K,V>)tab[index];for(; entry != null ; entry = entry.next) {if ((entry.hash == hash) && entry.key.equals(key)) {V old = entry.value;entry.value = value;return old;}}addEntry(hash, key, value, index);return null;}

通过Hashtable的put底层源码,我们可以看到,方法体内,首先就对value值进行的判空操作,如果为空则抛出空指针异常;其次在计算hash值的时候,直接调用key的hashCode()方法,若keynull,自然也会报空指针异常,因此,我们在调用put方法存储键值对时,key与value都非null。

HashMap的键值与null

我们同样也通过HashMap的put方法去分析它的底层源码,先上代码。

【源码解析2-hash()】

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

在计算hash值的时候,hashmap中通过三目运算符做了空值处理,直接返回0,这样最终计算出key应该存储在数组的第一位上,且key是唯一性呢,因此,key最多存一个null;

【源码解析3】

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {// 数组HashMap.Node<K,V>[] tab; // 元素HashMap.Node<K,V> p; // n 为数组的长度 i 为下标int n, i;// 数组为空的时候if ((tab = table) == null || (n = tab.length) == 0)// 第一次扩容后的数组长度n = (tab = resize()).length;// 计算节点的插入位置,如果该位置为空,则新建一个节点插入if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);///
}

回归putVal()方法,我们逐句阅读后也没有发现对于value值为null的处理与限定,因此,它可以存储为null的value值,我们知道HashMap的键值对特点如同身份证与人名一样,key等同于身份证,全国唯一,而value值等同于人名,可以重复,比如全国有上万个叫张伟的,所以value值也就同样允许存储多个null。

ConcurrentHashMap的键值与null

很多同学们可能会以为ConcurrentHashMap不过是HashMap在多线程环境下的版本,底层实现都一致,只是多了加锁的操作,所以二者对于null的允许程度是一样。
如果你是这样想,那可就完全错了,对于ConcurrentHashMap来说,它也不允许存储键值对为null的数据。
Doug Lea(ConcurrentHashMap的设计者)曾这样说道:

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.

大致的意思是,在单线程环境中,不会存在一个线程操作该 HashMap 时,其他的线程将该 HashMap 修改的情况,可以通过 contains(key)来做判断是否存在这个键值对,从而做相应的处理;
而在多线程环境下,可能会存在多个线程同时修改键值对的情况,这时是无法通过contains(key)来判断键值对是否存在的,这会带来一个二义性的问题,Doug Lea说二义性是多线程中不能容忍的!

啥是二义性? 咱们通俗点讲就是一个结果,2种释义,就好比我们通过get方法获取值的时候,返回一个null,其实我们是无法判断是值本身为null还是说集合中就没这个值!

所以说,ConcurrentHashMap的key和value均不可为null。

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

在这里插入图片描述
如果您想与Build哥的关系更近一步,还可以关注俺滴公众号“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

在这里插入图片描述

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

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

相关文章

【Java】面向对象之多态超级详解!!

文章目录 前言一、多态1.1 多态的概念1.2 多态的实现条件1.3 重写1.3.1方法重写的规则1.3.2重写和重载的区别 1.4 向上转型和向下转型1.4.1向上转型1.4.2向下转型 1.5 多态的优缺点1.5.1 使用多态的好处1.5.2 使用多态的缺陷 结语 前言 为了深入了解JAVA的面向对象的特性&…

基于yolov5的电瓶车和自行车检测系统,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的电瓶车和自行车检测系统_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的电瓶车和自行车检测系统是在pytorch框架下实现的&#xff0c;这是一个完整的项目&#xff0c;包括代码&#xff0c;数据集&#xff0c;训练好的模型…

协程库项目—日志模块

日志模块程序结构图 sylarLog ├── LogLevel&#xff08;日志级别封装类&#xff09; │ ├── 提供“从日志级别枚举值转换到字符串”、“从字符串转换相应的日志级别枚举值”等方法 ├── LogEvent&#xff08;日志事件类&#xff09; │ ├── 封装日志事件的属性…

Unity(第二十一部)动画的基础了解(感觉不了解其实也行)

1、动画组件老的是Animations 动画视频Play Automatically 是否自动播放Animate Physics 驱动方式&#xff0c;勾选后是物理驱动Culling Type 剔除方式 默认总是动画化就会一直执行下去&#xff0c;第二个是基于渲染播放&#xff08;离开镜头后不执行&#xff09;&#xff0c; …

高性能服务系列【二】CPU和内存

现代计算机的系统架构十分复杂。在服务器中&#xff0c;双路处理器已经十分常见。最近Arm处理器实现双路共384核心&#xff0c;要知道目前Linux内核最高只支持256核&#xff0c;这就有点尴尬。 多路处理器将越来越普遍&#xff0c;对性能的影响和传统架构有不小的差别&#xf…

MySQL中json类型的字段

有些很复杂的信息&#xff0c;我们一般会用扩展字段传一个json串&#xff0c;字段一般用text类型存在数据库。mysql5.7以后支持json类型的字段&#xff0c;还可以进行sql查询与修改json内的某个字段的能力。 1.json字段定义 ip_info json DEFAULT NULL COMMENT ip信息, 2.按…

GO基本类型一些记录

基本类型一些记录 1.直接粘贴文本进println(" ")2.中文字符串长度别用len( )3.byte本质是uint8 1.直接粘贴文本进println(" ") GoLand会自动补充转义符 2.中文字符串长度别用len( ) 用相应编码库的方法&#xff0c;一般utf8即可 utf8.RuneCountInStrin…

PySide6实现word转化pdf

目录 一:实现思路 二:实现代码 三:完整代码和界面 一:实现思路 利用PySide6创建两个按钮和一个显示区域,一个选择文件按钮,一个转化按钮和信息展示,操作文件按钮选择一个待转化的word文档。并且展示文件路径到信息展示区,操作转化按钮,读取选择的文件转化为pdf。并…

ThreadLocal介绍

文章目录 ThreadLocal源码分析&#xff1a;set方法get方法remove方法 ThreadLocal内存泄漏问题 ThreadLocal ThreadLocal提供了线程局部变量&#xff0c;每个线程都可以通过set和get方法来对这个变量进行操作&#xff0c;但不会和其他线程的局部变量冲突&#xff0c;实现了线程…

Doris实战——拈花云科的数据中台实践

目录 前言 一、业务背景 二、数据中台1.0—Lambda 三、新架构的设计目标 四、数据中台2.0—Apache Doris 4.1 新架构数据流转 4.2 新架构收益 五、新架构的落地实践 5.1 模型选择 5.1.1 Unique模型 5.1.2 Aggregate模型 5.2 资源管理 5.3 批量建表 5.4 计算实现…

Stable Diffusion 模型分享:Realistic Stock Photo(真实的库存照片)

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 条目内容类型大模型基础模型SDXL 1.0来源CIVITAI作者PromptSharingSamaritan文件名称reali…

原生GO开发的博客系统

Go博客实战教程&#xff0c;是一个练手级项目教程&#xff0c;使用原生Go开发&#xff0c;未使用任何框架。 如何使用原生Go开发一个web项目 循序渐进&#xff0c;掌握编程思维和思路 初步具有工程思维&#xff0c;能适应一般的开发工作 1. 搭建项目 package mainimport (&q…

Vue3_2024_1天【Vue3创建和响应式,对比Vue2】

前言&#xff1a; Vue3对比Vue2版本&#xff0c;它在性能、功能、易用性和可维护性方面都有显著的提升和改进。 性能优化&#xff1a;模板编译器的优化、对Proxy的支持以及使用了更加高效的Virtual DOM算法等。这使得Vue3的打包大小减少了41%&#xff0c;初次渲染提速55%&#…

【MATLAB源码-第153期】基于matlab的OFDM系统插入导频和训练符号两种信道估计方式误码率对比仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OFDM&#xff08;Orthogonal Frequency Division Multiplexing&#xff0c;正交频分复用&#xff09;是一种高效的无线信号传输技术&#xff0c;广泛应用于现代通信系统&#xff0c;如Wi-Fi、LTE和5G。OFDM通过将宽带信道划分…

使用docker方式测试部署django项目(客户催)

需求 1&#xff1a;已有django项目–weidanyewu 2&#xff1a;希望在服务器上测试部署–客户催 3&#xff1a;没完善django的启动 4&#xff1a;使用临时数据库进行演示 5&#xff1a;使用python3.10版本镜像 6&#xff1a;展示端口80 7&#xff1a;后台执行django程序 8&#…

【C语言】熟悉文件顺序读写函数

前言 本篇详细介绍了 文件顺序读写常用函数&#xff0c;快来看看吧~ 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 前言 ​编辑 文件顺序读写函数 fgetc函数 示例 fputc函数 逐个字符写入 写入26个字母 文…

手写模拟器,解放双手!效果炸裂的生产工具

手写模拟器是一款基于Handright的仿手写图片生成软件&#xff0c;可以让你的电脑和手机也能写出漂亮的手写字&#xff0c;你只需要输入你想要写的内容&#xff0c;选择你喜欢的字体和背景&#xff0c;就可以生成一张高仿真的手写图片&#xff0c;用于各种场合&#xff0c;比如做…

uniapp中canvas的基础使用

canvas简介 canvas是uniapp中提供的一个组件,用于生成自定义的图形界面。通过canvas,我们可以通过JavaScript代码在页面上绘制各种图形和图像。 使用canvas 在页面中添加canvas 首先需要在页面的template中添加一个canvas组件: <template><view><canvas ca…

linux:iostat 用法详解

文章目录 描述语法参数例子 描述 iostat 是一个在类Unix操作系统中常用的系统监控工具&#xff0c;尤其是Linux系统中&#xff0c;它主要用于收集和报告中央处理器(CPU)使用情况以及磁盘输入/输出(I/O)统计数据。以下是 iostat 命令的基本用法及其参数详解&#xff1a; 语法 …

代码随想录三刷 day11 | 栈与队列之 20. 有效的括号 1047. 删除字符串中的所有相邻重复项 150. 逆波兰表达式求值

三刷day11 20. 有效的括号1047. 删除字符串中的所有相邻重复项150. 逆波兰表达式求值 20. 有效的括号 题目链接 解题思路&#xff1a; 有三种不匹配的情况&#xff1a; 第一种情况&#xff0c;字符串里左方向的括号多余了 。 第二种情况&#xff0c;括号没有多余&#xff0c;…