HashMap在容量超过负载因子所定义的容量之后,就会扩容。java里的数组是无法自己扩容的,将HashMap的大小扩大为原来数组的两倍
我们来看jdk1.8扩容的源码

  final Node<K,V>[] resize() {//oldTab:引用扩容前的哈希表Node<K,V>[] oldTab = table;//oldCap:表示扩容前的table数组的长度int oldCap = (oldTab == null) ? 0 : oldTab.length;//获得旧哈希表的扩容阈值int oldThr = threshold;//newCap:扩容之后table数组大小//newThr:扩容之后下次触发扩容的条件int newCap, newThr = 0;//条件成立说明hashMap中的散列表已经初始化过了,是一次正常扩容if (oldCap > 0) {//判断旧的容量是否大于等于最大容量,如果是,则无法扩容,并且设置扩容条件为int最大值,//这种情况属于非常少数的情况if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}//设置newCap新容量为oldCap旧容量的二倍(<<1),并且<最大容量,而且>=16,则新阈值等于旧阈值的两倍else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold}//如果oldCap=0并且边界值大于0,说明散列表是null,但此时oldThr>0//说明此时hashMap的创建是通过指定的构造方法创建的,新容量直接等于阈值//1.new HashMap(intitCap,loadFactor)//2.new HashMap(initCap)//3.new HashMap(map)else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;//这种情况下oldThr=0;oldCap=0,说明没经过初始化,创建hashMap//的时候是通过new HashMap()的方式创建的else {               // zero initial threshold signifies using defaultsnewCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}//newThr为0时,通过newCap和loadFactor计算出一个newThrif (newThr == 0) {//容量*0.75float ft = (float)newCap * loadFactor;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指向新创建的数组table = newTab;//本次扩容之前table不为nullif (oldTab != null) {//对数组中的元素进行遍历for (int j = 0; j < oldCap; ++j) {//设置e为当前node节点Node<K,V> e;//当前桶位数据不为空,但不能知道里面是单个元素,还是链表或红黑树,//e = oldTab[j],先用e记录下当前元素if ((e = oldTab[j]) != null) {//将老数组j桶位置为空,方便回收oldTab[j] = null;//如果e节点不存在下一个节点,说明e是单个元素,则直接放置在新数组的桶位if (e.next == null)newTab[e.hash & (newCap - 1)] = e;//如果e是树节点,证明该节点处于红黑树中else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);//e为链表节点,则对链表进行遍历else { // preserve order//低位链表:存放在扩容之后的数组的下标位置,与当前数组下标位置一致//loHead:低位链表头节点//loTail低位链表尾节点Node<K,V> loHead = null, loTail = null;//高位链表,存放扩容之后的数组的下标位置,=原索引+扩容之前数组容量//hiHead:高位链表头节点//hiTail:高位链表尾节点Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;//oldCap为16:10000,与e.hsah做&运算可以得到高位为1还是0//高位为0,放在低位链表if ((e.hash & oldCap) == 0) {if (loTail == null)//loHead指向eloHead = e;elseloTail.next = e;loTail = e;}//高位为1,放在高位链表else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);//低位链表已成,将头节点loHead指向在原位if (loTail != null) {loTail.next = null;newTab[j] = loHead;}//高位链表已成,将头节点指向新索引if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;}

扩容之后原位置的节点只有两种调整
保持原位置不动(新bit位为0时)
散列原索引+扩容大小的位置去(新bit位为1时)
扩容之后元素的散列设置的非常巧妙,节省了计算hash值的时间,我们来看一 下具体的实现

当数组长度从16到32,其实只是多了一个bit位的运算,我们只需要在意那个多出来的bit为是0还是1,是0的话索引不变,是1的话索引变为当前索引值+扩容的长度,比如5变成5+16=21

这样的扩容方式不仅节省了重新计算hash的时间,而且保证了当前桶中的元素总数一定小于等于原来桶中的元素数量,避免了更严重的hash冲突,均匀的把之前冲突的节点分散到新的桶中去

HashMap的扩容方式相关推荐

  1. Redis遍历方式思考--字典扩容方式

    全量遍历keys 工作中线上Redis维护,有时候我们需要查询特定前缀的缓存key列表来手动处理数据.可能是修改值,删除key.那么怎么才能快速的从海量的key中查找到对应的前缀匹配项. Redis提 ...

  2. HashMap的扩容及树化过程

    上一篇博文 HashMap中capacity.loadFactor.threshold.size等概念的解释 讨论了HashMap中的一些基本概念,这篇博文结合具体示例,讨论下HashMap的扩容.树 ...

  3. HashMap的扩容机制

    HashMap的扩容机制 简介 单纯的kv键值对结构,可以接受null键和null值,速度比较快,非线程安全. HashMap的数据结构 HashMap实际上是一个"链表的数组"的 ...

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

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

  5. 看完这篇还不懂HashMap的扩容机制,那我要哭了~

    HashMap 发出的 Warning:这是<Java 程序员进阶之路>专栏的第 56 篇.那天,小二垂头丧气地跑来给我诉苦,"老王,有个学弟小默问我' HashMap 的扩容机 ...

  6. HashMap之扩容原理

    一.什么是HashMap? HashMap 数据结构为 数组+链表(JDk1.7),JDK1.8中增加了红黑树,其中:链表的节点存储的是一个 Entry 对象,每个Entry 对象存储四个属性(has ...

  7. 深入理解HashMap+ConcurrrentHashMap扩容的原理

    目录 1.JDK1.7版本的HashMap扩容机制(重要) 2.JDK1.8版本的HashMap扩容机制(重要) 2.JDK1.7版本的ConcurrentHashMap扩容机制 4.JDK1.8版本 ...

  8. 一种可以避免数据迁移的分库分表scale-out扩容方式

    原文地址:http://jm-blog.aliapp.com/?p=590 目前绝大多数应用采取的两种分库分表规则 mod方式 dayofweek系列日期方式(所有星期1的数据在一个库/表,或所有?月 ...

  9. HashMap的扩容机制---resize()

    扩容 就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素. 什么时候扩容? 当向容器添加元素的时 ...

最新文章

  1. 解决Redis超卖问题
  2. Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用
  3. 模块怎么用_IC设计方法:模块划分与overdesign
  4. javafx swing_Swing应用程序中的JavaFX 8 DatePicker
  5. Linux 命令之 iconv -- 转换文件的字符编码
  6. 为什么计算机打不开系统盘了,为什么我从装了系统之后进入‘我的电脑’发现F/E盘都打不开了。系统提示:ses.exe找不到!...
  7. fortran 读整行_我整周读过的最有趣的东西
  8. 大时代已过,小趋势可期
  9. 世界服务器系统竞赛,他们为何对ASC世界大学生超算竞赛情有独钟?
  10. 计算机二级考试能报考的科目,计算机二级考试有哪些科目可以选择报考?
  11. MySQL数据库设计常犯的错以及对性能的影响
  12. 全局最小割Stoer-Wagner算法
  13. Linux 入门记录:五、vi、vim 编辑器
  14. 如何在百度文库复制文字
  15. CentOS7更换阿里yum源
  16. PTAM入门、ROS下PTAM的安装配置及使用
  17. 彻底杀除“logo1_.exe”(威金病毒)病毒
  18. 下载书籍的网址【汇总】
  19. vue邮箱验证正则表达式错误:Unterminated regular expression
  20. Office-PPT如何使多张图片自动等距排列

热门文章

  1. 9.grep/egrep详解
  2. Pinpoint本地调试
  3. 阿里云盾导致 cgroup2 cpu 控制器丢失的坑
  4. c语言json数组转字符串数组,Json数组转换字符串、字符串转换原数组......
  5. 同一台服务器 不同站点 域名解析,域名是什么?一级域名和二级域名有什么关系?一个域名怎么做不同的网站?...
  6. 获取CSDN下载积分
  7. echarts里的seriesIndex 和dataIndex是什么意思
  8. centos安装htop
  9. JDK版本优化JDK1.2-12
  10. 2021-08-08-OIDC, Oauth2.0 , SMAL 的合而不同