Java8 HashMap扩容时为什么不需要重新hash
技巧: 与&操作 和 与 n 如8 与,为0 则位置不变
https://blog.csdn.net/zlp1992/article/details/104376309
java8在实现HashMap时做了一系列的优化,其中一个重要的优化即在扩容的时候,原有数组里的数据迁移到新数组里不需要重新hash,而是采用一种巧妙的方法,代码如下:
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
可以看到它是通过将数据的hash与扩容前的长度进行与操作,根据结果为0还是不为0来做对应的处理e.hash & oldCap。
举个例子
比如数据 A它经过hash之后的值为 0111(二进制),一开始map中数组的长度是 8,根据定位的逻辑 (n-1)&hash,那么数据A的位置是:
(n-1)&hash = (8-1) & 0111 = 111 & 0111 = 0111
扩容之后数组长度是原来的2倍,即16,假设我们重新算一遍A的位置,那么应该是:
(n-1)&hash =(16-1) & 0111 = 1111 & 0111 = 0111
可以看到数据A扩容之后,如果重新计算hash的话,它的位置是没有发生变化的,来看一下 e.hash & oldCap的结果
e.hash & oldCap = 0111 & 8 = 0111 & 1000 = 0
比如数据B它经过hash之后的值为 1111,在扩容之前数组长度是8,数据B的位置是:
(n-1)&hash = (8-1) & 1111 = 111 & 1111 = 0111
扩容之后,数组长度是16,重新计算hash位置是:
(n-1)&hash = (16-1) & 1111 = 1111 & 1111 = 1111
可见数据B的位置发生了变化,同时新的位置和原来的位置关系是:
新的位置(1111)= 1000+原来的位置(0111)=原来的长度(8)+原来的位置(0111)
继续看一下e.hash & oldCap的结果
e.hash & oldCap = 1111 & 8 = 1111 & 1000 = 1000 (!=0)
那么有没有看出规律呢,因为每次扩容都是2的倍数,计算位置的时候是和数组的长度-1做与操作,那么影响位置的数据只有最高的一位,比如 8-1 =7= 0111 ,16-1=15=1111 ,对于每个数据来说只有从右边数第四位的值会影响结果,当数据的hash的右边第四位为1的时候位置会发生变化,如上面的数据B,如果第四位为0,那么数据不会发生变化,如上面的数据A,而这个第四位 1000 恰好又是扩容前的数组长度,因此可以根据e.hash & oldCap的结果来判断,如果是0,说明位置没有发生变化,如果不为0,说明位置发生了变化,而且新的位置=老的位置+老的数组长度。
不过为什么当某个bin只有一个数据的时候要重新hash呢?
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
理论上也可以采用e.hash & oldCap这种方式?
Java8 HashMap扩容时为什么不需要重新hash的更多相关文章
- Java源码系列4——HashMap扩容时究竟对链表和红黑树做了什么?
我们知道 HashMap 的底层是由数组,链表,红黑树组成的,在 HashMap 做扩容操作时,除了把数组容量扩大为原来的两倍外,还会对所有元素重新计算 hash 值,因为长度扩大以后,hash值也随 ...
- jdk1.7中hashmap扩容时不会产生死循环
在扩容时 transfer( ) 方法中 newTable 新数组 局部变量 table 旧数组 全局变量 当第一个链表进行while循环时 执行到 e.next = newTable[i]; 时 n ...
- HashMap、Hashtable、HashSet三种hash集合的区别
转载:http://www.cnblogs.com/lzrabbit/p/3721067.html#h1 HashMap和Hashtable的区别 两者最主要的区别在于Hashtable是线程安全,而 ...
- Java8中HashMap扩容算法小计
Java8的HashMap扩容过程主要就是集中在resize()方法中 final Node<K,V>[] resize() { // ...省略不重要的 } 其中,当HashMap扩容完 ...
- JDK1.8 HashMap 扩容 对链表(长度小于默认的8)处理时重新定位的过程
关于HashMap的扩容过程,请参考源码或百度. 我想记录的是1.8 HashMap扩容是对链表中节点的Hash计算分析. 对术语先明确一下: hash计算指的确定节点在table[index]中的链 ...
- java 基础 --- java8 HashMap
问题 : HashMap 容量大小 (capacity)为什么为 2n HashMap 是线程安全的吗,为什么 HashMap 既然有hash进行排位还需要equals()作用是什么 文章部分图片 ...
- JAVA8 HashMap 源码阅读
序 阅读java源码可能是每一个java程序员的必修课,只有知其所以然,才能更好的使用java,写出更优美的程序,阅读java源码也为我们后面阅读java框架的源码打下了基础.阅读源代码其实就像再看一 ...
- Java8 HashMap详解
Java8 HashMap Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成. 根据 Java7 HashMap 的介绍,我们知道,查找的 ...
- Java8 HashMap源码分析
java.util.HashMap是最常用的java容器类之一, 它是一个线程不安全的容器. 本文对JDK1.8.0中的HashMap实现源码进行分析. HashMap使用位运算巧妙的进行散列并使用链 ...
随机推荐
- TensorFlow优化器浅析
本文基于tensorflow-v1.15分支,简单分析下TensorFlow中的优化器. optimizer = tf.train.GradientDescentOptimizer(learning_ ...
- tip7:CentOS8虚拟机安装相关总结
使用工具:Win10家庭版.WM12Pro.CentOS8. 一.安装 之前使用虚拟机安装操作系统使用的都是OpenSuse,也有相关备份.但是在Win10家庭版上用不了,启动电脑蓝屏(可能某些硬件不 ...
- 实战 | 关于随手一点就发现XSS漏洞这件事
起因 今天正在做一个项目时,需要找一个云接码平台接码去登录系统.当我随手从百度找了一个接码平台时,一个偶然的发现,有了今天这篇文章. 正文 当我进入这个接码平台随便找了个手机号,然后等了好几分钟都没啥 ...
- docker配置tomcat
docker pull tomcat tomcat的目录结构 /usr/local/tomcat/webapps web目录 /usr/local/tomcat/logs 日志目录 /usr/loca ...
- Codeforces Round #769 (Div. 2)D,E
D.New Year Concert 传送门 题目大意: 一个长为 N ( 1 ≤ N ≤ 2 × 1 0 5 ) N(1\leq N\leq2\times 10^5) N(1≤N≤2×105)的序列 ...
- 使用Filter过滤器,解决中文乱码问题
首先是建立一个filter.java文件,注意implements的是Filter的servlet的那个包: public class filter11 implements Filter { @Ov ...
- Python:读取txt中按列分布的数据,并将结果保存在Excel文件中 && 保存每一行的元素为list
import xlwt import os def write_excel(words,filename): #写入Excel的函数,words是数据,filename是文件名 wb=xlwt.Wor ...
- vue--按需加载的3种方式(解决网页首次加载速度慢的问题)
一.vue的异步组件加载 使用异步组件加载,打包的时候会将每个组件分开打包到不同的js文件中: {path: '/index', name: 'index', meta:{ title:'首页', r ...
- python中max使用key参数
arr = [1,2,2,2,2,3,3,3] arr = collections.Counter(arr) b = max(arr.keys(),key = arr.get) 这个时候b为arr中元 ...
- Linux CentOS7.X-安装mysql5.7数据库(安装包tar.gz)
一.下载对应版本的mysql 1.官网下载压缩安装包. 进入到mysql官网下载自己对应版本的mysql,下载地址:https://dev.mysql.com/downloads/mysql/5.7. ...