记录读源码-HashMap(2)

1.HashMap的扩容机制

  HashMap内部提供了resize()方法,该方法主要用于初始化生成table数组以及将原来的table数组(下面称为桶)进行扩容,HashMap按当前桶数组长度的2倍进行扩容。在扩容两倍之后, 原来数组中存放的Node要存放到新的数组中,重新计算键值对的位置,并把它们移动到合适的位置上去.  接下来我们来看看具体的实现:

  源码比较长,提取出来大概做了这几件事:

  1.重新计算数组大小newCap,以及新的临界值newThr;

  2.初始化新的数组,并将旧的数组中键值对节点重新映射到新的桶数组里

  对步骤1进行分解:

对步骤2中重新映射节点进行分解:

重新映射节点需要考虑节点类型。对于树形节点,需先拆分红黑树再映射。对于链表类型节点,则需先对链表进行分组,然后再映射。关于红黑树拆分的逻辑后面再说,先来看看链表是怎样进行分组映射的。

从源码中可以看出:元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置,也就是说原索引的位置和在原索引+oldCap的位置。看下图可以明白这句话的意思,n为table的长度,图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,其中hash1是key1对应的哈希与高位运算结果。

  元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化

2.树化

  链表树化是jdk1.8中HashMap新增加的一个优化,虽然降低了处理频繁的碰撞,代码复杂度也随之上升。笔者对红黑树不是很熟悉,只知道红黑树是一种自平衡的二叉查找树,本身就比较复杂。所以笔记不对红黑树展开描述,有需要了解请自行搜索学习。

  先来看一下链表转树的源码:

  从源码可以看到,当桶数组的长度较小时,如果节点超过阈值时,优先是扩容,优先扩容可以避免一些列的不必要的树化过程。同时,桶容量较小时,扩容会比较频繁,扩容时需要拆分红黑树并重新映射。所以在桶容量比较小的情况下,树化反而变成吃力不讨好,增加复杂度。HashMap具体怎么树化就不继续深入探究了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • HashMap 是 Java 面试必考的知识点,面试官从这个小知识点就可以了解我们对 Java 基础的掌握程度。网...
    野狗子嗷嗷嗷阅读 6,848评论 9 107
  • HashMap 源码分析 前几篇分析了 ArrayList , LinkedList ,Vector ,Stack...
    醒着的码者阅读 2,930评论 4 44
  • 学习资料; 《Java程序性能优化》 美团点评技术团队 Java 8系列之重新认识HashMap 张旭童大佬 面试...
    英勇青铜5阅读 2,956评论 3 97
  • 紫砂壶的使用,因其材质的特点,可以“蕴味育香”。品茗的间隙还可以握在手心,常加摩挲保养, 又可焕发出浑朴润雅的光泽...
    饮哲阅读 350评论 0 1
  • 目录 上一篇 “请苗小姐上茅山?就凭你们几个?”松明的话让雪菲儿很不爽,“你们几个如果嫌命长,正好可以把你们的狗命...
    刘白月阅读 984评论 0 1

友情链接更多精彩内容