【Java集合篇】HashMap的hash方法是如何实现的?

在这里插入图片描述

HashMap的hash方法是如何实现的?

  • ✔️ 典型解析
  • ✔️ 拓展知识仓
    • ✔️ 使用&代替%运算
    • ✔️扰动计算


✔️ 典型解析


hash 方法的功能是根据 Key 来定位这个K-V在链表数组中的位置的。也就是hash方法的输入应该是个Object类型的Key,输出应该是个int类型的数组下标。


最简单的话,我们只要调用 Object 对象的hashCode()方法,该方法会返回一个整数,然后用这个数对 HashMap 或者 HashTable 的容量进行取模就行了。只不过,在具体实现上,考虑到效率等问题,HashMap的实现会稍微复杂一点。他的具体实现主要由两个方法 int hash(Object k)intindexFor(int h,int length)来实现的 DK 1.8中不再单独有indexFor方法,但是在计算具体的tableindex时也用到了一样的算法逻辑,具体代码可以看putVal方法)


hash:该方法主要是将Object转换成一个整型


indexFor:该方法主要是将hash生成的整型转换成链表数组中的下标


在这里面,HashMap的hash方法为了提升效率,主要用到了以下技术手段:


1、使用位运算(&)来代替取模运算(%),因为位运算(&)效率要比代替取模运算(%)高很多,主要原因是位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。


2、对hashcode进行扰动计算,防止不同hashCode的高位不同但低位相同导致的hash冲突。简单点说,就是为了把高位的特征和低位的特征组合起来,降低哈希冲突的概率,也就是说,尽量做到任何位的变化都能对最终得到的结果产生影响


✔️ 拓展知识仓


✔️ 使用&代替%运算


不知道,大家有没有想过,为什么可以使用位运算(&)来实现取模运算(%)呢?


这实现的原理如下:


X%2 ^ n = X&(2^n-1)


2 ^ n 表示2的n次方,也就是说,一个数对2^n取模== 一个数和( 2 ^ n - 1)做按位与运算


假设n为3,则2 ^ 3=8,表示成2进制就是1000。2^3-1=7,即0111。


此时X&(2^3- 1)就相当于取X的2进制的最后三位数


从2进制角度来看,X/8相当于X>>3,即把X右移3位,此时得到了X/8的商,而被移掉的部分(后一位),则是X%8,也就是余数。


上面的解释不知道你有没有看懂,没看懂的话其实也没关系,你只需要记住这个技巧就可以了。或者你可以找几个例子试一下。


6 % 8 = 6,6 & 7 = 6


10 % 8 = 2,10 & 7 = 2


在这里插入图片描述

所以, return h & (length-1) ; 只要保证length的长度是 2^n 的话,就可以实现取模运算了。而HashMap中的length也确实是2的幂,初始值是16,之后每次扩充为原来的2倍。


链接: 【Java集合篇】为什么HashMap的Cap是2^n,如何保证?


总结一下,HashMap的数据是存储在链表数组里面的。在对HashMap进行插入/删除等操作时,都需要根据K-V对的键值定位到他应该保存在数组的哪个下标中。而这个通过键值求取下标的操作就叫做哈希。HashMap的数组是有长度的,Java中规定这个长度只能是2的幂,初始值为16。简单的做法是先求取出键值的hashcode,然后在将hashcode得到的int值对数组长度进行取模。为了考虑性能,Java总采用按位与操作实现取模操作。


其实,使用位运算代替取模运算,除了性能之外,还有一个好处就是可以很好的解决负数的问题。因为我们知道,hashcode的结果是int类型,而int的取值范围是-2^31 ~ 2^31 - 1,即[-2147483648,2147483647];这里面是包含负数的,我们知道,对于一个负数取模还是有些麻烦的。如果使用二进制的位运算的话就可以很好的避免这个问题。首先,不管hashcode的值是正数还是负数。length-1这个值一定是个正数。那么,他的二进制的第一位一定是(有符号数用最高位作为符号位,“0” 代表 “+” ,“1” 代表 “-”),这样里两个数做按位与运算之后,第一位一定是个0,也就是,得到的结果一定是个正数。


✔️扰动计算


其实,无论是用取模运算还是位运算都无法直接解决冲突较大的问题。


比如:CA11 0000 和 0001 0000 在对 0000 1111 进行按位与运算后的值是相等的。


在这里插入图片描述

两个不同的键值,在对数组长度进行按位与运算后得到的结果相同,这不就发生了冲突吗。那么如何解决这种冲突呢,来看下Java是如何做的。


其中的主要代码部分如下:


h ^= k.hashCode();
h ^= (h >>> 20)^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);

这段代码是为了对key的hashCode进行扰动计算,防止不同hashCode的高位不同但低位相同导致的hash冲突。简单点说,就是为了把高位的特征和低位的特征组合起来,降低哈希冲突的概率,也就是说,尽量做到任何一位的变化都能对最终得到的结果产生影响。


举个例子来说,我们现在想向一个HashMap中put 个K-V对,Key的值为“hollischuang”,经过简单的获取hashcode后,得到的值为“1011000110101110011111010011011”,如果当前HashTable的大小为16,即在不进行扰动计算的情况下,他最终得到的index结果值为11。由于15的进制扩展到32位为“00000000000000000000000000001111”,所以,一个数字在和他进行按位与操作的时候,前28位无论是什么,计算结果都一样(因为0和任何数做与,结果都为0) 。如下图。


在这里插入图片描述

可以看到,后面的两个hashcode经过位运算之后得到的值也是11 ,虽然我们不知道哪个key的hashcode是上面例子中的那两个,但是肯定存在这样的key,这就产生了冲突。


那么,接下来,我看看一下经过扰动的算法最终的计算结果会如何


在这里插入图片描述

从上面图中可以看到,之前会产生冲突的两个hashcode,经过扰动计算之后,最终得到的index的值不一样了,这就很好的避免了冲突。

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

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

相关文章

ConcurrentHashMap的原理分析学习

ConcurrentHashMap 的初步使用及场景 CHM 的使用 ConcurrentHashMap 是 J.U.C 包里面提供的一个线程安全并且高效的 HashMap,所以ConcurrentHashMap 在并发编程的场景中使用的频率比较高,那么这一节课我们就从ConcurrentHashMap 的使用上以及源码层面来…

【教学类-综合练习-05】20231226 大4班 数学综合题(X—Y加法减法、X乘法、X—Y数字分合)

背景需求 年终了,清理库存,各种打印的题型纸都拿出来,当个别化学习材料 教学过程: 时间:2023年12月26日 班级:大四班 人数:28人

Debezium发布历史49

原文地址: https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/ 欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯. 使用发件箱模式进行可靠的微服务数…

Unity3d 实现直播功能(无需sdk接入)

Unity3d 实现直播功能 需要插件 :VideoCapture 插件地址(免费的就行) 原理:客户端通过 VideoCapture 插件实现推流nodejs视频流转服务进行转发,播放器实现rtmp拉流 废话不多说,直接上 CaptureSource我选择的是屏幕录制,也可以是其他源 CaptureType选择LIVE–直播形式 LiveSt…

有没有比较好的制造业工单管理系统?

制造业公司由于要处理大量的售前售后工作,常常会使用不同的管理系统来协助管理,比如客户管理用的crm系统,人事管理的HR系统,设备管理和报修管理的工单系统等等。不同类型的系统,都有做得比较好的行业佼佼者&#xff0c…

关键字:package关键字

在 Java 中,package关键字用于组织和管理类文件。它将类文件分组到不同的包中,以提供更好的代码组织和可读性。 以下是package关键字的用法: 1.package语句:在 Java 源代码的开头使用package关键字来声明当前类所属的包。例如&a…

pycharm远程开发调试(remote development)踩坑记录2

在一次我清理了服务器上一些老的pycharm版本之后 打算重新装3.2版本,就全部给清理了。结果坏了事了,新版的装不上了。 试了公司和中科院的服务器都出现这样的问题,100%复现。md。 一直在这一步循环: Downloading the IDE Backen…

算法的复杂度分析

[王有志](https://www.yuque.com/wangyouzhi-u3woi/dfhnl0/hqrch62un0cc9sp2?singleDoc# 《🔥快来关注我》),一个分享硬核Java技术的互金摸鱼侠加入Java人的提桶跑路群:[共同富裕的Java人](https://www.yuque.com/wangyouzhi-u3woi/dfhnl0/n…

Nacos 学习之系列文章

系列文章目录 目录 系列文章目录 文章目录 前言 一、Nacos是什么? 二、Nacos的主要功能 服务发现和服务健康监测 动态配置服务 动态 DNS 服务 三、Nacos 地图 四、Nacos 生态图 总结 前言 Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Naco…

如何创建百度百科词条?想要创建百度百科的朋友看这里!(十年百度百科创建经验分享)

随着互联网的普及,越来越多的人开始关注网络信息的传播。在这个过程中,百度百科作为一个重要的知识分享和展示平台,扮演着举足轻重的角色。百度百科已经成为了人们获取知识的重要途径。所以无论是品牌、企业还是人物,都想在百度百…

C#编程-实现函数重载

考虑一个示例:您必须编写一个程序来实现计算器的功能。计算器执行各种运算,例如数字的加、减及乘等。可以对任何类型的数据执行这些运算。这是否意味着您必须定义单独的函数名(如addInteger、addFloat和addDoublie)对每种此类数字…

STM32 内部 EEPROM 读写

STM32 的某些系列 MCU 自带 EEPROM。笔者使用的 STM32L151RET6 自带 16 KB 的 EEPROM,可以用来存储自定义的数据。在芯片选型时,自带 EEPROM 也可以作为一个考量点,省去了在外接 EEPROM 的烦恼。 下面简单介绍下 STM32 内部 EEPROM 的读写流…

Kubernetes Gateway API V1.0:您应该切换吗?

自Kubernetes Gateway API 发布 v1.0以来已经过去两个多月了,这标志着其一些关键 API 已经进入普遍可用状态。 去年,当网关 API升级为测试版时,我曾写过有关该 API的文章,但一年后,问题仍然存在。您是否应该从 Ingres…

多功能号卡推广分销管理系统 流量卡推广分销网站源码-目前市面上最优雅的号卡系统

一套完善,多功能,的号卡分销系统,多接口,包括运营商接口,无限三级代理,最简单易用的PHP~ 目前市面上最优雅的号卡系统!没有之一 软件架构说明 环境要求php7.3以上(建议低于8.0),MySQL5.6以上,Nginx1.16(无要求) 产品特性 自动安装向导 易于安装使用部署 多个第…

安全与认证Week4

目录 目录 Web Security (TLS/SSL) 各层安全协议 Transport Layer Security (TLS)传输层安全性(TLS) SSL和TLS的联系与区别 TLS connection&session 连接与会话 题目2答案点 TLS ArchitectureTLS架构(5个协议) 题目1答案点 Handshake Proto…

云原生技术专题 | 解密2023年云原生的安全优化升级,告别高危漏洞、与数据泄露说“再见”(安全管控篇)

背景介绍 2023年,我们见证了科技领域的蓬勃发展,每一次技术革新都为我们带来了广阔的发展前景。作为后端开发者,我们深受其影响,不断迈向未来。 随着数字化浪潮的席卷,各种架构设计理念相互交汇,共同塑造了…

SpringCloud系列篇:核心组件之负载均衡组件

🥳🥳Welcome Huihuis Code World ! !🥳🥳 接下来看看由辉辉所写的关于SpringCloud的相关操作吧 目录 🥳🥳Welcome Huihuis Code World ! !🥳🥳 一.负载均衡组件是什么 二.负载均衡…

小程序实现绘制图片 保存到手机

HTML <template><view><canvas canvas-id"myCanvas" :style"{height:380px,width:wWidthpx,background:#FFFFFF}"></canvas><view class"textCenter"><button click"saveCanvas">保存图片</b…

OpenHarmony沙箱文件

一.前言 1.前景提要 DevEcoStudio版本&#xff1a;DevEco Studio 3.1 Release SDK版本&#xff1a;3.2.2.5 API版本&#xff1a;9 2.概念 在openharmony文件管理模块中&#xff0c;按文件所有者分类分为应用文件和用户文件和系统文件。 1&#xff09;沙箱文件。也叫做应…

学习Redis缓存

学习Redis缓存 NoSQL和SQL的区别缓存缓存作用缓存成本添加Redis缓存 Redis特征Redis中数据结构Redis通用命令String类型Key的层级格式Hash类型Redis的Java客户端 NoSQL和SQL的区别 缓存 缓存就是数据交换的缓冲区&#xff0c;是存储数据的临时地方&#xff0c;一般读写性比较高…