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

news2024/11/15 21:47:53

写在开头

今天在写《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.coloradmin.cn/o/1483901.html

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

相关文章

ARM架构基础内容

文章目录 前言一、AAPCS二、中断调用过程三、FreeRTOS中任务切换的实质总结 前言 一、AAPCS AAPCS&#xff0c;全称为ARM Architecture Procedure Call Standard&#xff0c;是ARM架构下的过程调用标准。这个标准定义了在ARM架构中函数调用和返回的约定&#xff0c;以确保不同…

文件底层的深入理解(1)

一、文件学习的简单铺垫 我们都知道&#xff0c;文件等于文件内容加上文件属性。访问文件之前都得先通过进程才能打开相应的文件&#xff0c;一个进程可以打开多个文件。修改文件&#xff0c;都是通过执行代码的方式完成修改。要对文件进行修改&#xff08;或其他操作&#xff…

Java 数组(详细)

目录 一、数组的概述 1. 数组的理解&#xff1a; 2. 数组相关的概念&#xff1a; 3. 数组的特点&#xff1a; 4. 数组的分类&#xff1a; 5.数据结构&#xff1a; 二、一维数组 1. 一维数组的声明与初始化 2. 一维数组元素的引用&#xff1a; 3. 数组的属性&#xff1…

微信小程序,h5端自适应登陆方式

微信小程序端只显示登陆(获取opid),h5端显示通过账户密码登陆 例如: 通过下面的变量控制: const isWeixin ref(false); // #ifdef MP-WEIXIN isWeixin.value true; // #endif

力扣hot100题解(python版44-47题)

44、二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1a;…

FHE 的应用场景

参考文献&#xff1a; [MSM22] Marcolla C, Sucasas V, Manzano M, et al. Survey on fully homomorphic encryption, theory, and applications[J]. Proceedings of the IEEE, 2022, 110(10): 1572-1609. 文章目录 FHEFirst GenerationSecond GenerationThird GenerationFour…

编码规则转换

思考&#xff1a; 如何将一个机内码转换为区内码&#xff1f; 只要将机内码减去 A0A0 就可以啦 如果只让我们用加法器来解决呢&#xff1f; 注意我们的数据占用了 32 位&#xff0c;如果想用补码进行减法运算的话&#xff0c;符号位怎么办&#xff1f;&#xff1f;&#xf…

2199. 骑士共存问题(最小割,最大权独立集,二分图)

活动 - AcWing 在一个 n∗n 个方格的国际象棋棋盘上&#xff0c;马&#xff08;骑士&#xff09;可以攻击的棋盘方格如图所示。 棋盘上某些方格设置了障碍&#xff0c;骑士不得进入。 对于给定的 n∗n个方格的国际象棋棋盘和障碍标志&#xff0c;计算棋盘上最多可以放置多少个…

2024年腾讯云优惠券_代金券_云服务器折扣券免费领取链接

腾讯云优惠代金券领取入口共三个渠道&#xff0c;腾讯云新用户和老用户均可领取8888元代金券&#xff0c;可用于云服务器等产品购买、续费和升级使用&#xff0c;阿腾云atengyun.com整理腾讯云优惠券&#xff08;代金券&#xff09;领取入口、代金券查询、优惠券兑换码使用方法…

UE5中实现后处理深度描边

后处理深度描边可以通过取得边缘深度变化大的区域进行描边&#xff0c;一方面可以用来做角色的等距内描边&#xff0c;避免了菲尼尔边缘光不整齐的问题&#xff0c;另一方面可以结合场景扫描等特效使用&#xff0c;达到更丰富的效果&#xff1a; 后来解决了开启TAA十字线和锯齿…

【go从入门到精通】go包,内置类型和初始化顺序

大家好&#xff0c;这是我给大家准备的新的一期专栏&#xff0c;专门讲golang&#xff0c;从入门到精通各种框架和中间件&#xff0c;工具类库&#xff0c;希望对go有兴趣的同学可以订阅此专栏。 go基础 。 Go文件名&#xff1a; 所有的go源码都是以 ".go" 结尾&…

latex宏包pythontex使用简明手册

文章目录 1.背景2. 基本示例2.1 hello world2.1.1 代码与输出结果2.1.2 说明2.1.3 如何编译使用了pythontex宏包的Latex文档2.1.4 如何既输出python代码又输出代码执行结果 2.2 在Latex中嵌入python变量的值2.2.1 示例代码与文档输出2.2.2 说明 2.3 在latex文档中输出控制台会话…

2.29号的复盘开始商城项目的规格如何设施的方式

第五次设计方式 我本来想的是&#xff0c;按照对于的组方式解拆分&#xff0c;通过分组处理的内存不同查询对应的手机上 出现问就是这里 对于的组相应规格相应里面有对于的价格&#xff0c;价格也就相同&#xff0c;无法进行区分 查询出来的对应的结果 还是没有办法具体拆分…

现货大宗商品发售平台搭建须知

在搭建现货大宗商品发售平台时&#xff0c;需要考虑以下关键因素&#xff1a; 目标市场分析&#xff1a;首先要明确你的平台将服务于哪些大宗商品市场&#xff0c;如农产品、金属、能源等。了解这些市场的特点、参与者、交易规则等&#xff0c;有助于你设计出更符合市场需求的…

解析/区分MOS管的三个引脚G、S、D(NMOS管和PMOS管)

MOS管的三个引脚分别是Gate&#xff08;栅极&#xff09;、Source&#xff08;源极&#xff09;和Drain&#xff08;漏极&#xff09;。以下是详细介绍&#xff1a; Gate&#xff08;栅极&#xff09;。这是控制MOS管开关的关键引脚&#xff0c;用于控制电流的流通。Source&…

pikachu验证XXE漏洞

先随便输入一个内容查看 接下来用bp抓包看下参数 有个xml参数&#xff0c;而且Content-Type: application/x-www-form-urlencoded&#xff0c;我们传入url编码后的xml内容试一下 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE foo [<!EN…

社区店选址人流量标准:如何确保充足的顾客流量

在选择社区店的位置时&#xff0c;确保充足的顾客流量是至关重要的。 作为一名开鲜奶吧5年的创业者&#xff0c;我将分享一些关于社区店选址人流量标准的关键要点&#xff0c;帮助你找到最适合的店铺位置。 1、研究人口统计学数据 了解潜在顾客的人口特征是选址的基础。通过研…

Unity3D学习之Lua热更新解决方案(二)XLua

文章目录 1 XLua概述2 xLua导入和AB包相关准备3 C#调用Lua3.1 Lua解析器3.2 文件加载重定向3.3 Lua解析器管理器3.3.1 重定向AB包内的Lua3.3.2 获得_G大表 3.4 全局变量的获取3.5 全局函数的获取3.5.1 无参无返回3.5.2 有参有返回3.5.3 多返回值3.5.4 变长参数 3.6 List和Dicti…

C 嵌入式系统设计模式 18:临界区模式

本书的原著为&#xff1a;《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》&#xff0c;讲解的是嵌入式系统设计模式&#xff0c;是一本不可多得的好书。 本系列描述我对书中内容的理解。本文章描述嵌入式并发和资源管理模式之四…

【AIGC】OpenAI推出王炸级模型sora,颠覆AI视频行业(2024)

对于OpenAI推出的Sora模型&#xff0c;我们可以进一步探讨其可能的技术细节、潜在应用以及对AI视频行业的影响。 点击以下任一云产品链接&#xff0c;跳转后登录&#xff0c;自动享有所有云产品优惠权益&#xff1a; 经过笔者亲测&#xff0c;强烈推荐腾讯云轻量应用服务器作…