JDK1.8中HashMap的hash算法和寻址算法
JDK 1.8 中 HashMap 的 hash 算法和寻址算法
HashMap 源码
hash() 方法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
h = key.hashCode()
表示 h 是 key 对象的 hashCode 返回值;
h >>> 16
是 h 右移 16 位,因为 int 是 4 字节,32 位,所以右移 16 位后变成:左边 16 个 0 + 右边原 h 的高 16 位;
最后把这两个进行异或返回。
异或:二进制位运算。如果一样返回 0,不一样则返回 1。
例:两个二进制 110 和 100 进行异或
110
^ 100
结果= 010
putVal() 中寻址部分
tab[i = (n - 1) & hash]
tab 就是 HashMap 里的 table 数组 Node<K,V>[] table
;
n 是这个数组的长度 length;
hash 就是上面 hash() 方法返回的值;
为什么不直接用 hashCode() % length ?
看完源码会有疑问,为什么不直接用 key 对象的 hashCode 对哈希表长度取模?
寻址为什么不用取模?
对于上面寻址算法,由于计算机对比取模,与运算会更快。所以为了效率,HashMap 中规定了哈希表长度为 2 的 k 次方,而 2^k-1 转为二进制就是 k 个连续的 1,那么 hash & (k 个连续的 1)
返回的就是 hash 的低 k 个位,该计算结果范围刚好就是 0 到 2^k-1,即 0 到 length - 1,跟取模结果一样。
也就是说,哈希表长度 length 为 2 的整次幂时, hash & (length - 1)
的计算结果跟 hash % length
一样,而且效率还更好。
为什么不直接用 hashCode() 而是用它的高 16 位进行异或计算新 hash 值?
int 类型占 32 位,可以表示 2^32 种数(范围:-2^31 到 2^31-1),而哈希表长度一般不大,在 HashMap 中哈希表的初始化长度是 16(HashMap 中的 DEFAULT_INITIAL_CAPACITY),如果直接用 hashCode 来寻址,那么相当于只有低 4 位有效,其他高位不会有影响。这样假如几个 hashCode 分别是 210、220、2^30,那么寻址结果 index 就会一样而发生冲突,所以哈希表就不均匀分布了。
为了减少这种冲突,HashMap 中让 hashCode 的高位也参与了寻址计算(进行扰动),即把 hashCode 高 16 位与 hashCode 进行异或算出 hash,然后根据 hash 来做寻址。
JDK 源码中 HashMap 的 hash 方法原理是什么?
JDK1.8中HashMap的hash算法和寻址算法的更多相关文章
- JDK1.8中HashMap实现
JDK1.8中的HashMap实现跟JDK1.7中的实现有很大差别.下面分析JDK1.8中的实现,主要看put和get方法. 构造方法的时候并没有初始化,而是在第一次put的时候初始化 putVal方 ...
- JDK1.7中HashMap死环问题及JDK1.8中对HashMap的优化源码详解
一.JDK1.7中HashMap扩容死锁问题 我们首先来看一下JDK1.7中put方法的源码 我们打开addEntry方法如下,它会判断数组当前容量是否已经超过的阈值,例如假设当前的数组容量是16,加 ...
- 记一次诡异的bug调试——————关于JDK1.7和JDK1.8中HashSet的hash(key)算法的区别
现象: 测试提了一个bug,我完全复现不了,但是最吊诡的是在其他人的机器上都可以复现.起初以为是SVN合并后出现的冲突,后来经过对比法排查: step 1: 我本地开两个jetty,一个跑合并之前的版 ...
- JDK1.7中HashMap底层实现原理
一.数据结构 HashMap中的数据结构是数组+单链表的组合,以键值对(key-value)的形式存储元素的,通过put()和get()方法储存和获取对象. (方块表示Entry对象,横排表示数组ta ...
- 关于JDK1.7+中HashMap对红黑树场景的思考
背景 在1.7之前的版本,当数组元素较多(几百.几千,或者更多)的时候,在这种前提扩容,涉及全量元素的遍历和坐标的重新定位,这个耗时会比较长.这是之前存在的一个弊端吧.那么引入红黑树之后就解决了问题, ...
- jdk1.8中hashmap的扩容resize
当hashmap第一次插入元素.元素个数达到容量阀值threshold时,都会扩容resize(),源码: (假设hashmap扩容前的node数组为旧横向node数组,扩容后的node数组为新横向n ...
- Java中HashMap的hash分布策略的简单解释
趴源码是看到一段不可思议的代码,网上的解释似乎不大令人满意,因此稍微花点时间解读了一下,如有错误请指正 HashMap的桶是这样搞的 // 片段1 static final int hash(Obje ...
- jdk1.7中hashmap扩容时不会产生死循环
在扩容时 transfer( ) 方法中 newTable 新数组 局部变量 table 旧数组 全局变量 当第一个链表进行while循环时 执行到 e.next = newTable[i]; 时 n ...
- hashMap在jdk1.7与jdk1.8中的原理及不同
在分析jdk1.7中HashMap的hash冲突时,不知大家是否有个疑问就是万一发生碰撞的节点非常多怎么版?如果说成百上千个节点在hash时发生碰撞,存储一个链表中,那么如果要查找其中一个节点,那就不 ...
随机推荐
- Lambda 表达式遍历集合时用remove方法删除list集合中满足条件的元素问题
一:循环遍历list集合的四种方式 简单for循环 iterator循环 增加for循环 Lanbda表达式 二:四种遍历方式的用法示例 //简单for循环 List<SalaryAdjustm ...
- 选课系统<基于面向过程\对象>
2020-04-15 00:09:28 程序目录: import os BASE_PATH=os.path.dirname(os.path.dirname(__file__)) DB_PATH=os. ...
- element-ui(vue)upload组件的http-request方法的使用
element-ui(vue)upload组件的http-request方法的使用 官方文档: http-request方法有一个默认的参数 content content 是一个object对象:里 ...
- 数据可视化之powerBI技巧(十二)学会这几个度量值,轻松获取前N名
数据中的明细项一般都有很多,可是我们关注的往往只是前几名,所以在报表中只展示关注的部分,就十分常用. 有了上篇(这几个示例,帮你深入理解RANKX排名)关于排名的铺垫,仅显示前N名就简单多了. 依然以 ...
- 如何用HMS Nearby Service给自己的App添加近距离数据传输功能
当你给朋友发送手机资料时,过了很久进度条却动也不动:当你想发送大文件给同事时,仅一个文件就用光了你所有流量:当你跟朋友乘坐飞机时想一起玩游戏时,却因没有网络无奈放弃. 们生活中似乎经常能遇到这 ...
- 使用nvm安装node,运行node报错 node: command not found
1. 使用nvm安装node之后,直接运行node命令会报错 node: command not found 需要使用nvm ls 查询一下当前使用的安装的node版本,然后使用node use 版 ...
- Ethical Hacking - POST EXPLOITATION(4)
PIVOTING Use the hacked device as a pivot. Try to gain access to other devices in the network. Tool: ...
- docker环境部署mysql
参考文档 docker官方:https://hub.docker.com/_/mysql/?tab=description 部署步骤 1. 拉取镜像 这里我拉取了tag为5.7的镜像 docker p ...
- socket链接
服务端: package com.batch.service.impl; import java.io.BufferedReader; import java.io.BufferedWriter; i ...
- Go操作腾讯云COS对象存储的简单使用案例
准备环境 安装Go环境 Golang:用于下载和安装 Go 编译运行环境,请前往 Golang 官网进行下载 安装SDK go get -u github.com/tencentyun/cos-go- ...