HashMap 中 split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) 方法详解

「这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战

1. 前言

这个方法主要是处理,hashMap在扩容时,底层数组的元素是一个TreeNode的时候(即该元素下的节点树化,这里不懂的可以看我的前两篇文章。java HashMap 详解 -- (默认常量和构造函数)HashMap 中的 resize() 方法详解) 这个方法主要作用:将树容器中的节点拆分为较低的树容器和较高的树容器,如果现在太小则取消树化。注意:它只能从 resize() 方法中调用

它是java.util.HashMap.TreeNode#split 中的方法

2. 源码讲解

  1. 在resize 方法中调用的源码:

图片.png

  1. 此方法源码

图片.png

  • map : 这里的map结合前面分析,就是当前调用的hashMap
  • tab : hashMap 扩容后的新数组
  • j : 当前这个treeNode 在原先hashMap oldTab 中的索引(这里不懂的可以看我上一篇文章
  • bit : 为扩容前的容量,即oldTab的长度
  • b : 这里保存当前数组索引下的元素,前面已经判断过,所以此元素必为 TreeNode 类型。
  • 这里和前面一样, lohead : 低位首节点, loTail : 低位尾节点
  • hiHead : 高位首节点, hiTail : 高位尾节点
  • lc:地位红黑树的节点数,hc:高位红黑树的节点数,它们地大小决定了红黑树是否要回转链表。
  • 遍历当前这个treeNode
  • next:记录e.next,然后把e.next 置null
  • 这里截图没截好,源代码:if ((e.hash & bit) == 0) {
  • 这里使用 e.hash& bit,因为bit是2地幂数,所以它的二进制形式为:1,10,100,1000 ...这种,所以只要当该元素重新分配还是在低位上,才会出现结果等于0,则说明它的hash值小于老数组的长度,即他再次分配还是会在原先老数组的位置上(即 低位数组)。所以如果低位尾结点为null ,就把它赋给 低位头结点,否则就赋给低位尾结点的next。最后使得 低位尾结点 = e。lc++,记录当前位置地节点数
  • 这里 当 e.hash & bit 不为0,则说明这个元素分配的位置必在,新数组的高位上,所以当高位尾结点为null,直接把该元素赋给高位头结点,否则赋给高位尾结点的next,最后把高位尾结点元素置为 e。hc++,记录当前位置地节点数
  • 如果 低位头节点不为null
    • 判断它是否小于 UNTREEIFY_THRESHOLD(它等于6,当当前索引下的元素数量小于等于它时,会使用链表代替,红黑树,即取消树化,具体实现在 lohead.untreeify(map) 中,并把最终处理结果放在 索引为index的元素中。
    • 否则还是让它使用红黑树的形式存储。
  • 如果 高位头节点不为null
    • 判断它是否小于 UNTREEIFY_THRESHOLD,这里和上面一样取消树化,不过因为是高位,索引存储的索引变成了 index+bit 即当前索引加上 oldTab的长度
    • 否则的话,把结果保存在 tab[index+bit] 中,进行树化处理

猜你喜欢

转载自juejin.im/post/7036330765424001038
int