JDK1.8 HashMap 扩容 对链表(长度小于默认的8)处理时重新定位的过程
关于HashMap的扩容过程,请参考源码或百度。
我想记录的是1.8 HashMap扩容是对链表中节点的Hash计算分析.
对术语先明确一下:
hash计算指的确定节点在table[index]中的链表位置index,不是节点的hash值。
Node<K,V> loHead = null, loTail = null; //这两个是记录重新hash计算后仍在原位置(设为index)的节点
Node<K,V> hiHead = null, hiTail = null; //这两个是记录重新hash计算后在原位置加上原容量 的位置的节点(index + old capacity)
那么问题来了 , 怎么就确定 扩容前的 链表节点 在 扩容后的位置 是 当前位置或者+old capacity的位置 ?
先看 put 节点时 对key查找在table位置的计算方法,在putVal中有这么一行(红色部分):
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; 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);
else {
其中 (n-1) & hash 就是在table中的位置(n即capacity),暂时将这个记为 oldIndex = (n-1) & hash (公式1);
再来看扩容时的代码,代码有点多,我删去一些不影响理解的还是看红色部分:
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
} threshold = newThr; Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
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; (公式4)
}
}
}
}
}
return newTab;
}
为了简单(二进制可以短点)起见,以 oldCapacity = 8 为例,扩容后 capacity = 16
newCap = oldCap << 1 : 新的容量capacity= 8<<1 = 16
newTab[e.hash & (newCap - 1)] , 红色部分即 原节点在 新table中的位置 , 暂时记为 newIndex = e.hash & (newCap - 1) (公式2)
hoHead,hoTail,hiHead,hiTail 不多说,上面有说明。 那么 这个下面这个判断条件,就决定了节点在扩容以后的位置
(e.hash & oldCap) == 0 (公式3)
最后再看是如何分配者两个链表的,一个原位置,一个j+oldCap位置。
下面分析是如何这么确定的:
总结上面所描述的,有下列几个变量
oldCapacity 用 n 代替
oldIndex = (n-1) & hash (公式1)
newIndex = (n*2 -1) & hash (公式2)
condition = n & hash (公式3)
几个值之间的关系:
if(condition == 0) 那么 newIndex 等于 oldIndex
else newIndex 等于 oldIndex + n
里面有一个隐含条件: n 是 2 的整数倍(也是满足关系的必要条件)
当n = 8 时, 其二进制 b1 = 1000 , 减去1 之后(7) 得到的二进制 b2 = 0111
扩容后, 即n*2 = 16的二进制是 10000 , 减去1之后(15) 得到的二进制是 b3 = 01111
假如hash的二进制是hash = 10111 ,
10111 & 1000 (b1) = 0 == conditon
10111 & 0111 (b2) = 111 == oldIndex
10111 & 01111 (b3) = 111 == newIndex
可以发现, 当 condtion 为0 时, hash的二进制 的 第4位 必然为 0 ,高位无所谓什么值 ,
hash &b2 (111) 结果必然是hash低三位的值,hash & b2 (1111) ,由于hash第4位为0 ,那么结果必然仍是hash低三位的值。
所以,当condition为0时, newIndex 必然等于 oldIndex
假如hash的二进制是hash = 11111,
11111 & 1000 (b1) = 1000 == condition = 8
11111 & 0111 (b2) = 00111 == oldIndex = 7
11111 & 01111 (b3) = 01111 == newIndex = 15
发现了吗? oldIndex 和 newIndex 差的就是 一个第 4 位的1 ,那么这个1 就是 2^4 = 8 , 也就是 oldCapacity (2^4) 的值。
无论多长的hash值,关键的一个二进制 在 第 x 位 (2^x = oldCapacity), 也就是这一位决定了 扩容前后的位置。
由于这样计算呢, java1.8中的HashMap 可以 不用在扩容的时候 一直往头节点插 (有环回的逻辑所以在多线程扩容的时候才会出现 闭环链 ),
1.8中对链表只有往后添加节点,没有环回的逻辑, 也就不可能在多线程的时候出现闭环链。
虽然时间复杂度是一样的,但是更机智了。
JDK1.8 HashMap 扩容 对链表(长度小于默认的8)处理时重新定位的过程的更多相关文章
- 关于JDK1.8 HashMap扩容部分源码分析
今天回顾hashmap源码的时候发现一个很有意思的地方,那就是jdk1.8在hashmap扩容上面的优化. 首先大家可能都知道,1.8比1.7多出了一个红黑树化的操作,当然在扩容的时候也要对红黑树进行 ...
- jdk1.8 HashMap扩容原理详解
JDK1.7中,resize时,index取得时,全部采用重新hash的方式进行了.JDK1.8对这个进行了改善. 以前要确定index的时候用的是(e.hash & oldCap-1),是取 ...
- jdk1.8 HashMap 实现 数组+链表/红黑树
转载至 http://www.cnblogs.com/leesf456/p/5242233.html 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Ja ...
- JDK1.8 HashMap源码分析
一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...
- JDK1.8 HashMap 源码分析
一.概述 以键值对的形式存储,是基于Map接口的实现,可以接收null的键值,不保证有序(比如插入顺序),存储着Entry(hash, key, value, next)对象. 二.示例 public ...
- (转)为什么HashMap中链表长度超过8会转换成红黑树
原博地址:https://blog.csdn.net/xingfei_work/article/details/79637878 HashMap在jdk1.8之后引入了红黑树的概念,表示若桶中链表元素 ...
- Java源码系列4——HashMap扩容时究竟对链表和红黑树做了什么?
我们知道 HashMap 的底层是由数组,链表,红黑树组成的,在 HashMap 做扩容操作时,除了把数组容量扩大为原来的两倍外,还会对所有元素重新计算 hash 值,因为长度扩大以后,hash值也随 ...
- jdk1.8 HashMap源码讲解
1. 开篇名义 jdk1.8中hashMap发生了一些改变,在之前的版本中hsahMap的组成是数组+链表的形式体现,而在1.8中则改为数组+链表+红黑树的形式实现,通过下面两张图来对比一下二者的不同 ...
- jdk1.8 HashMap底层数据结构:深入解析为什么jdk1.8 HashMap的容量一定要是2的n次幂
前言 1.本文根据jdk1.8源码来分析HashMap的容量取值问题: 2.本文有做 jdk1.8 HashMap.resize()扩容方法的源码解析:见下文“一.3.扩容:同样需要保证扩容后的容量是 ...
随机推荐
- fabric-ca1.03安装
前面的文档已经成功的安装了fabric1.0.1的e2e例子.之后代码换成1.0.3版本按步骤重新安装一下,就可以切换到1.0.3了.1.0.3的脚本和启动命令没有变化,还是用的1.0.1的. 1.准 ...
- Geany 编辑器打开 高亮所选单词 功能
Geany 编辑器打开 高亮所选单词 功能 在Ubuntu 系统的Software Center 工具中,搜索到geany, 下方有个 Miscellanous Plugins for Geany, ...
- Redis、MongoDB及Memcached的区别
Redis(内存数据库) 是一个key-value存储系统(布式内缓存,高性能的key-value数据库).和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).l ...
- liunx poi excel下载内容乱码本地tomcat正常
结论:在jsp中加上out.clear即可(前提保证生成的excel在服务器上是正确的,只是浏览器传输才出现乱码). dowload.jsp完整代码 <%@ page language=&quo ...
- mybatis_动态sql 查询、更新
1)sql where 条件 select id="find" parameterType="User" resultType="User" ...
- git push error HTTP code = 413
error: RPC failed; HTTP 413 curl 22 The requested URL returned error: 413 Request Entity Too Large 将 ...
- java 常用第3方工具
https://www.cnblogs.com/chenpi/p/5608628.html#_label4
- compute by 的使用
GROUP BY子句有个缺点,就是返回的结果集中只有合计数据,而没有原始的详细记录.如果想在SQL SERVER中完成这项工作,可以使用COMPUTE BY子句.COMPTE生成合计作为附加的汇总列出 ...
- 机器学习进阶-图像基本操作-图像数据读取 1.cv2.imread(图片读入) 2.cv2.imshow(图片展示) 3.cv2.waitKey(图片停留的时间) 4.cv2.destroyAllWindows(清除所有的方框界面) 5.cv2.imwrite(对图片进行保存)
1. cv2.imread('cat.jpg', cv2.IMGREAD_GRAYSCALE) # 使用imread读入图像(BGR顺序), 使用IMGREAD_GRAYSCALE 使得读入的图片为 ...
- JEECG 3.7.3 新春版本发布,企业级JAVA快速开发平台
JEECG 3.7.3新春版本发布 - 微云快速开发平台 导读 ⊙精美Echart报表 ⊙二维码生成功能 ⊙Online接口改造采用JWT机制 ⊙智能菜单搜索 ⊙代码生成器模板优 ...