文章目录

  • HashMap底层的扩容机制
  • resize扩容
  • resize源码
  • 源码文字说明
  • HashMap底层为什么是2倍扩容?

HashMap底层的扩容机制

resize扩容

HashMap会在两个地方进行resize(扩容):

1 ,HashMap实行了懒加载, 新建HashMap时不会对table进行赋值, 而是到第一次插入时, 进行resize时构建table;
2, 当HashMap.size 大于 threshold时, 会进行resize;threshold的值,当第一次构建时, 如果没有指定HashMap.table的初始长度, 就用默认值16, 否则就是指定的值; 然后不管是第一次构建还是后续扩容, threshold = table.length * loadFactor;

在Java8的扩容中,不是简单的将原数组中的每一个元素取出进行重新hash映射,而是做移位检测。所谓移位检测的含义具体是针对HashMap做映射时的&运算所提出的,通过上文对&元算的分析可知,映射的本质即看hash值的某一位是0还是1,当扩容以后,会相比于原数组多出一位做比较,由多出来的这一位是0还是1来决定是否进行移位,而具体的移位距离,也是可知的,及位原数组的大小,我们来看下表的分析,假定原表大小16。

由上表可知,是否移位,由扩容后表示的最高位是否1为所决定,并且移动的方向只有一个,即向高位移动。因此,可以根据对最高位进行检测的结果来决定是否移位,从而可以优化性能,不用每一个元素都进行移位

resize源码

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){// 如果当前哈希桶容量超过最大值2^30,直接返回旧哈希桶大小
// 到达上线 threshold 设置最大阈值返回 表示之后就不再扩容了,随便存,随便hash冲
// 突去,就这么大,无限增加红黑树节点了if(oldCap>=MAXIMUM_CAPACITY){threshold=Integer.MAX_VALUE;return oldTab;}
// 按照两倍扩容后,如果容量没有达到上限
// 并且旧容量已经超过16
// newCap翻倍,则按照两倍的方式扩容else if((newCap=oldCap<<1)<MAXIMUM_CAPACITY &&oldCap>=DEFAULT_INITIAL_CAPACITY)
// 下一次扩容时参考,达到该阈值则扩容newThr=oldThr<<1; // double threshold}else if(oldThr>0) // initial capacity was placed in thresholdnewCap=oldThr;else{ // zero initial threshold signifies using defaults
// 将新容量设置为默认值16
// 将扩容阈值设置为0.75*16newCap=DEFAULT_INITIAL_CAPACITY;newThr=(int)(DEFAULT_LOAD_FACTOR*DEFAULT_INITIAL_CAPACITY);}
// 如果新阈值为0,按照新容量的大小重新计算下一次扩容时的阈值
// 计算方式:采用新容量 * 负载因子
// 即扩容的时机为:当元素个数超过阈值时则扩容if(newThr==0){float ft=(float)newCap*loadFactor;
// 如果新容量和阈值都是在2^30以内,下一次库哦哦荣的阈值则为ft
// 否则改为最大值newThr=(newCap<MAXIMUM_CAPACITY &&ft< (float)MAXIMUM_CAPACITY?(int)ft:Integer.MAX_VALUE);}
// 更新下次扩容的上线threshold=newThr;
@SuppressWarnings({"rawtypes", "unchecked"})
// // 申请更多的桶,将旧哈希桶中节点转移到新哈希桶中
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 orderNode<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;elseloTail.next=e;loTail=e;}else{if(hiTail==null)hiHead=e;elsehiTail.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;}

源码文字说明

1 如果table == null, 则为HashMap的初始化, 生成空table返回即可;

2 如果table不为空, 需要重新计算table的长度, newLength = oldLength << 1(注, 如果原oldLength已经到了上限, 则newLength = oldLength);

3 遍历oldTable:

3.1 首节点为空, 本次循环结束;

3.1 首节点不为空无后续节点, 重新计算hash位, 本次循环结束;

3.2 当前是红黑树, 走红黑树的重定位;

3.3 当前是链表, JAVA7时还需要重新计算hash位, 但是JAVA8做了优化, 通过(e.hash & oldCap) == 0来判断是否需要移位; 如果为真则在原位不动, 否则则需要移动到当前hash槽位 + oldCap的位置;

HashMap底层为什么是2倍扩容?


第一是因为哈希函数的问题
通过除留余数法方式获取桶号,因为Hash表的大小始终为2的n次幂,因此可以将取模转为位运算操作,提高效率,容量n为2的幂次方,n-1的二进制会全为1,位运算时可以充分散列,避免不必要的哈希冲突,这也就是为什么要按照2倍方式扩容的一个原因
第二是因为是否移位的问题
是否移位,由扩容后表示的最高位是否1为所决定,并且移动的方向只有一个,即向高位移动。因此,可以根据对最高位进行检测的结果来决定是否移位,从而可以优化性能,不用每一个元素都进行移位因为为0说明刚好在移位完之后的位置,为1说明不是需要移动oldCop,这也是其为什么要按照2倍方式扩容的第二个原因。

HashMap底层的扩容机制(以及2倍扩容的原因)相关推荐

  1. HashMap底层扩容机制是2倍的原理

    HashMap底层扩容2倍原理 总所周知,获取分配的两种计算公式: 内存中获取分区位置:hash(key.hashCode) % (length - 1) HashMap获取数据存储的位置:hash( ...

  2. HashMap扩容机制解读

    扩容机制 什么时候需要扩容: 当hashmap中的元素个数超过数组大小 * loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值(DEFAULT_LOAD_FACTOR) ...

  3. Solana扩容机制分析(1):牺牲可用性换取高效率的极端尝试 | CatcherVC Research

    摘要 Solana扩容主要基于:高效利用网络带宽.减少节点间通讯次数.加快节点运算速度 三大方面,这些措施直接缩短了出块和共识通讯的时间,但也降低了系统可用性(安全). Solana提前公开出块者Le ...

  4. Vector的扩容机制

    到需要扩容的时候,Vector会根据需要的大小,创建一个新数组,然后把旧数组的元素复制进新数组. 我们可以看到,扩容后,其实是一个新数组,内部元素的地址已经改变了.所以扩容之后,原先的迭代器会失效: ...

  5. 阿里P8架构师谈:深入探讨HashMap的底层结构、原理、扩容机制

    摘要 HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型. 随着JDK(Java Developmet Kit)版本的更新,JDK1.8对HashMap底层的实现进行了优化, ...

  6. HashMap的底层结构、原理、扩容机制

    文章目录 一.问题 Q0:HashMap是如何定位下标的? Q1:HashMap由什么组成? Q2:Java的HashMap为什么不用取余的方式存储数据? Q3:HashMap往链表里插入节点的方式? ...

  7. 深入探讨HashMap的底层结构、原理、扩容机制

    Q0:HashMap是如何定位下标的? A:先获取Key,然后对Key进行hash,获取一个hash值,然后用hash值对HashMap的容量进行取余(实际上不是真的取余,而是使用按位与操作,原因参考 ...

  8. Java8 HashMap 扩容机制与线程安全分析

    如果大家有仔细阅读过 HashMap 的源码就会发现 HashMap 的哈希表初始化并不是在其构造函数中进行的,而是 resize() 方法. 这篇文章不对 HashMap 中的树进行介绍. 一.Ha ...

  9. hashmap扩容机制_图文并茂:HashMap经典详解!

    点击上方 Java后端,选择 设为星标 优质文章,及时送达 代码中的注解多看几遍,其中HashMap的扩容机制是要必懂知识!结合图片一起理解! 什么是 HashMap? HashMap 是基于哈希表的 ...

最新文章

  1. Windows定时删除某天前文件的批处理脚本
  2. matlab mbuild setup,关于mbuild的一个问题
  3. Cpp 对象模型探索 / 对象访问成员变量的原理
  4. 专用计算机例子,计算机基础综合题案例.doc
  5. JavaScript实现hamiltonianCycle哈密尔顿图算法(附完整源码)
  6. c++STL(标准模板库)理论基础
  7. apple tv设置_如何设置Apple HomePod
  8. linux之chattr命令
  9. 对Oracle SQL Developer中 变量的学习
  10. 《数学之美》一 文字、数字及语言的历史
  11. wifi分析仪怎么看哪个信道好_专业的WiFi检测工具有哪些?如何解决wifi信号不好?...
  12. MD5 算法的加密、撞库破解及Python实现和操作演示
  13. excel超链接报本机限制问题
  14. 写在2016的最后一周
  15. tar解压时提示Cannot open: File exists
  16. 使用吉特哈布Codespaces和Visual Studio Code进行C++开发
  17. jetson的学习资料总结
  18. linux系统下 USB 摄像头1080分辨率采集帧率低问题的解决方法
  19. 俞敏洪一分钟励志演讲
  20. Java基础语法(汉罗塔)

热门文章

  1. Java厘米级高精准定位系统源码(支持UWB、蓝牙、WIFI定位)
  2. 人人都应该读的好书--推荐《上帝与拉里·埃里森的不同》 (转)
  3. GitOps 体系学习和理解
  4. 贵州计算机专业专科学校,贵州职业学校计算机应用技术专业
  5. AI绘画爆火,人工智能会取代艺术吗
  6. 在uni-app中onShareTimeline转发朋友圈不起作用之解决方案
  7. 349两个数组的交集(遗留问题)
  8. 我的工作效率是如何提升的?
  9. ThinkPHP include失效所发现的大小写敏感系列惨案
  10. 台式计算机喇叭啸叫声,话筒系统的啸叫声的原因和处理办法