HashMap中hash方法的作用(详解)

首先,hash方法用来干什么?

在搞清楚原理之前,我们先站在巨人的肩膀浅浅了解一下hash方法的本质作用。

实质上,它的作用很朴素,就是用key值通过某种方式计算出一个hash码

而且这个hash码我们后面要用来计算key存在底层数组的下标,所以它必须保持一定的随机性,让计算出的数组元素下标更加均衡分布,减少碰撞,其实就是更大程度上的避免hash冲突。

注:保持随机性这一性质,只有在hash方法计算hash值这一步会有所体现,后面的取模运算只是单独的为了取到余数,与保证随机性没有关系

Hash方法是什么?

这里先直观的给出它的源码:

参数 key 需要计算哈希码的键值。
key == null ? 0 : (h = key.hashCode()) ^ (h >>> 16) :这是一个三目运算符,看似复杂,其实很好理解。
逻辑就是:如果键值为null,则哈希码为 0 (依旧是说如果键为 null ,则存放在第一个位置);否则,通过调用 hashCode() 方 法获取键的哈希码,并将其与右移16 位的哈希码进行异或运算。
^ 运算符:异或运算符是 Java 中的一种位运算符,它用于将两个数的二进制位进行比较,如果相同则为 0,不同则为 1
 >>> 16 将哈希码向右移动 16 位,相当于将原来的哈希码分成了两个 16 位的部分。最终返回的是经过异或运算后得到的哈希码值。

这短短的一行代码,汇聚不少计算机巨佬们的聪明才智。

理论上,哈希值(即hashcode方法返回的值)是一个 int 类型,大家都知道int型在java中占4个字节,即32位,范围从 -2147483648 2147483648 。前后加起来大概 40 亿的映射空间,只要哈希值映射得比较均匀松散,一般是不会出现哈希碰撞(哈希冲突会降低 HashMap 的效率)。
但问题是一个 40 亿长度的数组,内存是放不下的。 HashMap 扩容之前的数组初始大小只有 16 ,所以这个哈希值是不能直接拿来用的,用之前要和数组的长度做取模运算(前文提到的 (n - 1) & hash ),用得到的余数来访问数组下标才行。

对h ^ h>>>16如何理解?

上面的h其实就是我们调用key的hashcode()方法得到的hashcode值,我们将它右移16位(因为是int型,所以总共是32位),前面16位补0,然后我们让它与原本的hashcode值进行异或,因为异或的逻辑是不同为1,相当于就是前16位与后16位进行会进行一次异或,占据最终返回的hash的后16位,然后前16位与进行补位的16个0进行异或,占据最终返回的hash的前16位,得到最终return的hash码。

上面的流程可以参考下面的图进行思考:

 

这样大家可能会比较疑惑,这么翻来覆去是为了啥呀,其实都是为了一个:hash码的随机性,目的还是为了避免hash冲突,毕竟hash冲突会降低HashMap的效率。

综上所述, hash 方法是用来做哈希值优化的 ,把哈希值右移 16 位,也就正好是自己长度的一半,之后与原哈希值做异或运算,这样就混合了原哈希值中的高位和低位,增大了随机性。

 

计算好了hash值,然后我们怎么使用hash值计算下标?

我们使用(n - 1)& hash的方式获取下标;(其实就是取模运算)

大家可能会比较好奇,要进行取模运算难道不该用% 吗?为什么要用位运算 &呢

这是因为%虽然确实可以实现,但是实际效率却还是不如&

并且我们的&能够取代%,是要有一个前提条件的:

只有当 b 2 n 次方时,才存在这样一个公式:a % b = a & (b-1)

我们可以对其进行一个验证:

我们来验证一下,假如 a = 14 b = 8 ,也就是 2^3 n=3
14%8 (余数为 6), 14 的二进制为 1110 8 的二进制 1000 8-1 = 7 7 的二进制为 0111 1110&0111=0110 ,也就是 0 * 2^0+1 * 2^1+1 * 2^2+0 * 2 ^3=0+2+4+0=6 14%8 刚好也等于 6
害,计算机就是这么讲道理,没办法
也就是说,只有数组长度是2的n次方时,使用&才会取余成功,这也是为什么,我们对hashmap进行扩容时,必须是2倍扩容了
为什么会这么巧,刚好二者就相同了呢,这与一个低位掩码的概念有关,这里我们先不细讲。

顺着上一个问题的图,我们再看这个步骤进行理解:

 

知道了hash方法的原理以及并将其计算出来,我们还需要知道:

 hash方法在哪里被用到?

1.首先当然是我们最经典的put方法 ,后面的putval会通过hash值来计算下标

2.其次是我们使用get方法获取元素时,会调用getNode方法,其中用到hash值

小结

hash 方法的主要作用是将 key 的 hashCode 值进行处理,得到最终的哈希值。由于 key 的hashCode 值是 不确定的,可能会出现哈希冲突,因此需要将哈希值通过一定的算法映射到HashMap 的实际存储位置上。
hash 方法的原理是,先获取 key 对象的 hashCode 值,然后将其高位与低位进行异或操作,得到一个新的哈希值。为什么要进行异或操作呢?因为对于 hashCode 的高位和低位,它们的分布是比较均匀的,如果只是简单地将它们加起来或者进行位运算,容易出现哈希冲突,而异或操作可以避免这个问题。然后将新的哈希值取模(mod),得到一个实际的存储位置。这个取模操作的目的是将哈希值映射到桶(Bucket)的索引上,桶是 HashMap 中的一个数组,每个桶中会存储着一个链表(或者红黑树),装载哈希值相同的键值对(没有相同哈希值的话就只存储一个键值对)。
总的来说,HashMap 的 hash 方法就是将 key 对象的 hashCode 值进行处理,得到最终的哈希值,并通过一定的算法映射到实际的存储位置上。这个过程决定了 HashMap 内部键值对的查找效率。

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

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

相关文章

golangd\pycharm-ai免费代码助手安装使用gpt4-免费使用--[推荐]

golangd-ai免费代码助手安装使用,pycharm可以使用,估计只要是xx的ide都是可以使用这个插件 目前GPT4以及gpt的大规模使用,如何快速掌握以及在ide中快速使用的办法,今天安装一款golangd编辑器的插件已经使用 一、安装以及使用 1.在golangd中…

贼全! 一举通关的 Spring+SpringBoot+SpringCloud 全攻略, 是真香啊

前几天,有幸从朋友那里得到了一份 Alibaba 内部的墙裂推荐的“玩转 Spring 全家桶的 PDF”,我也不是个吝啬的人,好的东西当然要一起分享。那今天我就秀一把,带你一站通关 Spring、Spring Boot 与 Spring Cloud,让你轻松斩获大厂 O…

Statefulset部署应用

上一部分我们分享到了使用 RS 没有办法让自己管理的多个 pod 都有一个独立的持久化声明,RS 没有办法在指定模板中对不同的 pod 做差异化处理 使用多个 RS 来分别管理自己的的一个 pod,当我们扩缩容的时候,也会出现问题,老的 pod …

C# 关于使用newlife包将webapi接口寄宿于一个控制台程序、winform程序、wpf程序运行

C# 关于使用newlife包将webapi接口寄宿于一个控制台程序、winform程序、wpf程序运行 安装newlife包 Program的Main()函数源码 using ConsoleApp3; using NewLife.Log;var server new NewLife.Http.HttpServer {Port 8080,Log XTrace.Log,SessionLog XTrace.Log }; serv…

【微服务架构设计】微服务不是魔术:处理超时

微服务很重要。它们可以为我们的架构和团队带来一些相当大的胜利,但微服务也有很多成本。随着微服务、无服务器和其他分布式系统架构在行业中变得更加普遍,我们将它们的问题和解决它们的策略内化是至关重要的。在本文中,我们将研究网络边界可…

使用贝叶斯算法完成文档分类问题

贝叶斯原理 贝叶斯原理(Bayes theorem)是一种用于计算条件概率的数学公式。它是以18世纪英国数学家托马斯贝叶斯(Thomas Bayes)的名字命名的。贝叶斯原理表达了在已知某个事件发生的情况下,另一个事件发生的概率。具体…

二十三种设计模式第十七篇--迭代子模式

迭代子模式是一种行为型设计模式,它允许你按照特定方式访问一个集合对象的元素,而又不暴露该对象的内部结构。迭代子模式提供了一种统一的方式来遍历容器中的元素,而不需要关心容器的底层实现。 该模式包含以下几个关键角色: 迭…

计算机网络(1) --- 网络介绍

目录 1.介绍协议 基础知识 协议 协议分层 OSI七层模型 2.TCP/IP五层模型 3.网络传输的基本流程 1.基本知识 协议报头 2.局域网通信的基本流程 3.网络传输流程 局域网分类 跨路由器传输 数据包封装和分用 4.网络中的地址管理 1.IP地址 2.MAC地址 3.区别 1.介绍…

训练自己的行文本检测EAST模型

训练自己的行文本检测EAST模型 训练数据格式 训练数据格式

模糊神经网络机械故障诊断(MATLAB代码)

效果 用训练好的模糊神经网络对机械故障进行诊断,根据网络的预测值得到机械的技术状态。预测值小于 1.5 时为正常状态,预测值在 1.5~2.5 之间时为曲轴轴承轻微异响,预测值在 2.5~3.5 之间时为曲轴轴承严重异响预测值在 3.5~4.5 之间时为连杆轴承轻微异响,预测值大于 4.5 时为连…

选好NAS网络储存解决方案,是安全储存的关键

随着网络信息的发展,NAS也越来越受到企业的关注,NAS网络存储除了提供简单的存储服务外,还可以提供更好的数据安全性、更方便的文件共享方式。但市面上的产品种类繁多,我们该如何选择合适的产品,通过企业云盘&#xff0…

高效协作处理缓存清理需求:生产者-消费者模式助力多模块缓存管理

在现代应用系统中,缓存是提高性能和减少数据库负载的重要手段之一。然而,缓存的数据在某些情况下可能会过期或者变得无效,因此需要及时进行清理。在复杂的应用系统中,可能有多个系统、多个模块产生缓存清理需求,而这些…

建造者模式——复杂对象的组装与创建

1、简介 1.1、概述 建造者模式又称为生成器模式,它是一种较为复杂、使用频率也相对较低的创建型模式。建造者模式向客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品。 建造者模式是较为复杂的创建型模式,它将客户端与包…

三个常用查询:根据用户名 / token查询用户信息+链表分页条件查询

目录 1.根据用户名或者token查询用户信息 会员信息实体类 统一状态Result类 controller层 service层及实现类 dao层 测试: 2.链表分页条件查询 会员等级实体类 封装条件类PageVo controller层 service层及实现类 dao层 Mapper.xml层 测试 vue前端参考 1.根据用户名…

【MySQL】表的内外连接

目录 一、内连接二、外连接2.1 左外连接2.2 右外连接 三、OJ题 表的连接分为内连和外连 一、内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选,我们前面学习的查询都是内连接,也是在开发过程中使用的最多的连接查询。 语法&#x…

汉明距离,两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

题记: 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 给你两个整数 x 和 y,计算并返回它们之间的汉明距离。 示例 1: 输入:x 1, y 4 输出:2 解释: 1 (0 0 0 1) 4 (0 1 0 0…

现在运动耳机什么牌子的好用、最好的运动耳机推荐

对于注重身体健康的小伙伴来说,每周必然都少不了有规律的运动,而运动的时候耳边没有音乐的陪伴总是稍显枯燥无味,很难让人提起干劲来。有些小伙伴觉得运动的时候戴着耳机,稍微跳动几下耳机就开始松动,随时都要分心提防…

QT 作业 day4 7/28

1.思维导图 2.手动完成服务器实现 .h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器 #include <QTcpSocket> //连接客户端套接字 #include <QMessageBox> #include <QList> //套接字容器 #include &l…

无涯教程-jQuery - show( )方法函数

show()方法仅显示匹配元素中的每个元素(如果隐藏)。此方法还有另一种形式&#xff0c;可以控制动画的速度。 show( ) - 语法 selector.show( ); show( ) - 示例 以下是一个简单的示例&#xff0c;简单说明了此方法的用法- <html><head><title>The jQuer…

Java-数组的定义和使用

一、数组的基本概念 1.1 为什么要使用数组 假设现在要存5个学生的javaSE考试成绩&#xff0c;并对其进行输出&#xff0c;则可有 public static void main(String[] args){ int score1 70; int score2 80; int score3 85; int score4 60; …