技巧: 与&操作   和   与 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的更多相关文章

  1. Java源码系列4——HashMap扩容时究竟对链表和红黑树做了什么?

    我们知道 HashMap 的底层是由数组,链表,红黑树组成的,在 HashMap 做扩容操作时,除了把数组容量扩大为原来的两倍外,还会对所有元素重新计算 hash 值,因为长度扩大以后,hash值也随 ...

  2. jdk1.7中hashmap扩容时不会产生死循环

    在扩容时 transfer( ) 方法中 newTable 新数组 局部变量 table 旧数组 全局变量 当第一个链表进行while循环时 执行到 e.next = newTable[i]; 时 n ...

  3. HashMap、Hashtable、HashSet三种hash集合的区别

    转载:http://www.cnblogs.com/lzrabbit/p/3721067.html#h1 HashMap和Hashtable的区别 两者最主要的区别在于Hashtable是线程安全,而 ...

  4. Java8中HashMap扩容算法小计

    Java8的HashMap扩容过程主要就是集中在resize()方法中 final Node<K,V>[] resize() { // ...省略不重要的 } 其中,当HashMap扩容完 ...

  5. JDK1.8 HashMap 扩容 对链表(长度小于默认的8)处理时重新定位的过程

    关于HashMap的扩容过程,请参考源码或百度. 我想记录的是1.8 HashMap扩容是对链表中节点的Hash计算分析. 对术语先明确一下: hash计算指的确定节点在table[index]中的链 ...

  6. java 基础 --- java8 HashMap

    问题 : HashMap 容量大小 (capacity)为什么为 2n HashMap 是线程安全的吗,为什么 HashMap 既然有hash进行排位还需要equals()作用是什么   文章部分图片 ...

  7. JAVA8 HashMap 源码阅读

    序 阅读java源码可能是每一个java程序员的必修课,只有知其所以然,才能更好的使用java,写出更优美的程序,阅读java源码也为我们后面阅读java框架的源码打下了基础.阅读源代码其实就像再看一 ...

  8. Java8 HashMap详解

    Java8 HashMap Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成. 根据 Java7 HashMap 的介绍,我们知道,查找的 ...

  9. Java8 HashMap源码分析

    java.util.HashMap是最常用的java容器类之一, 它是一个线程不安全的容器. 本文对JDK1.8.0中的HashMap实现源码进行分析. HashMap使用位运算巧妙的进行散列并使用链 ...

随机推荐

  1. Spring高级特性之四:FactoryBean和BeanFactory

    FactoryBean和BeanFactory两只是两个单词顺序不同但是内容大不相同.落脚点在后面一个单词,前面一个单词是其功能描述:FactoryBean--工厂bean,一个建工厂的bean?Be ...

  2. Java ClassLoader 学习笔记

    参考 Java类加载器(ClassLoader)

  3. 学着去看开 你不是一个low程序员

    http://www.cocoachina.com/programmer/20160627/16835.html 看完这篇文章 感谢作者

  4. CentOS7更新OpenSSH8

    今天使用漏洞扫描工具扫描了一下系统漏洞,发现有一个openssh版本的漏洞.所以本着安全的原则修复一下. 第一时间打开百度搜索相关内容,大多数是编译安装的.每个人的环境和版本也不一样.有一定连不上去的 ...

  5. 关于maven依赖中的scope的作用和用法

    举例如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>s ...

  6. 2021顶级的开源 BI(商业智能)软件和报表工具

    在这个信息化时代,每分每秒都产生海量数据.在海量数据中,挖掘出有用的数据,并且能以较人性化.直观的方式展示这些数据,变得尤为重要.本文将介绍5款顶级开源 BI(商务智能)软件和报表工具,用于商业数据的 ...

  7. Java -- List与数组转换

    list转数组 使用for循环 使用list.toArray(new String[]),不可以强制转换list.toArray(),因为数组在jvm是一个object表示的,是一个对象 数组转lis ...

  8. linux 解决磁盘占用100%

    df -h 查看磁盘使用情况 ll -h 查看文件的大小   使用如下命令查找大于100M的大文件,发现有几个日志文件及临时文件比较大,使用rm –rf删除即可.   find / -size +10 ...

  9. idea教程--使用maven创建web项目

    1.单击create new project 2.运行maven项目 在pom.xml文件中添加tomcat插件然后如下图运行;

  10. 浏览器无插件播放rtsp流解决方案

    1. 安装 FFmpeg 参考 CentOS下安装FFmpeg,特别详细. 我遇到的错误和解决办法: 缺少lame ffmpeg+libmp3lame库源码安装教程(CentOS) make ffmp ...