写点什么

ConcurrentHashMap 扩容?lastRun 到底是个啥?,理解透彻

作者:Java高工P7
  • 2021 年 11 月 09 日
  • 本文字数:1969 字

    阅读完需:约 6 分钟

//lastRun 最终要处理的节点


Node<K, V> lastRun = f;// 尾节点,且和头节点的 hash 值取于不相等


//遍历当前 bucket 的链表,目的是尽量重用 Node 链表尾部的一部分


//从这里一直到下面 set 可以不用看,就是生成两个链,一个低位链,一个高位链


//下一个节点......下一个的下一个


//一条链上的 hash 值相等吗? 不相等 putval 的时候 (n-1)&hash 只是为了找到位置。


//这个 for 循环找到 lastRun,从 lastRun 开始后面的要在低位都在低位,要在高位都在高位


for (Node<K, V> p = f.next; p != null; p = p.next) {


// 取于桶中每个节点的 hash 值


//下一个节点的 hash&n 当前节点的 hash&n,因为一直循环所以就是尾结点和头结点的 hash&n 比较


int b = p.hash & n;


// 如果节点的 hash 值和首节点的 hash 值取于结果不同


if (b != runBit) {


runBit = b;// 更新 runBit 为尾结点的,用于下面判断 lastRun 该赋值给 ln 还是 hn。


lastRun = p;// 这个 lastRun 保证后面的节点与自己的取于值相同,避免后面没有必要的循环,因为上面是从 p 开始循环的。


// 所以这里到 lastrun 就不循环了


}


}


if (runBit == 0) {//如果最后更新的 runBit,设置低位节点


ln = lastRun;


hn = null;


} else {//否则,设置高位节点


hn = lastRun;// 如果最后更新的 runBit 是 1, 设置高位节点


ln = null;


}


//构造高位以及低位的链表


// 再次循环,生成两个链表,lastRun 作为停止条件,这样就是避免无谓的循环(lastRun 后面都是相同的取于结果)


// 将原本的一个链表根据 hash&n 分为 2 个链表,构建新链表采用头插法


// 无法概括两个新链表相对旧链表的顺序,有很多可能,并不是一个正序,一个倒序


// 这个 for 循环,把 lastRun 前面装配到高位节点或者低位节点


for (Node<K, V> p = f; p != lastRun; p = p.next) {


int ph = p.hash;


K pk = p.key;


V pv = p.val;


// 如果与运算结果是 0,那么就还在低位


if ((ph & n) == 0)// 如果是 0 ,那么创建低位节点


ln = new Node<K, V>(ph, pk, pv, ln);


else // 1 则创建高位


hn = new Node<K, V>(ph, pk, pv, hn);


}


setTabAt(nextTab, i, ln);//将低位的链表放在 i 位置也就是不动 低位链不需要变


setTabAt(nextTab, i + n,


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


hn);//将高位链表放在 i+n 位置,n 是数组的长度,是假如当前 14 14+16=30


setTabAt(tab, i, fwd);//把旧 table 的 hash 桶中放置转发节点,表明此 hash 桶已经被处理


advance = true;


}


问题一:第一个 for 循环什么意思?


===============================================================================


for (Node<K, V> p = f.next; p != null; p = p.next) {


// 取于桶中每个节点的 hash 值


//下一个节点的 hash&n 当前节点的 hash&n,因为一直循环所以就是尾结点和头结点的 hash&n 比较


int b = p.hash & n;


// 如果节点的 hash 值和首节点的 hash 值取于结果不同


if (b != runBit) {


runBit = b;// 更新 runBit 为尾结点的,用于下面判断 lastRun 该赋值给 ln 还是 hn。


lastRun = p;// 这个 lastRun 保证后面的节点与自己的取于值相同,避免后面没有必要的循环,因为上面是从 p 开始循环的。


// 所以这里到 lastrun 就不循环了


}


}


找到 lastRun,从 lastRun 开始后面的要在低位都在低位,要在高位都在高位


避免下面的 lastRun 又要反复的去计算一遍 hash 值(第二个循环中),如果 lastRun 后面很多的话,还是很客观的。


问题二:第二个 for 循环中为什么以 lastRun 作为结束标志?


=============================================================================================


问题一中给了答案,因为 lastRun 后面 runBit 是一样的,不需要单独进入 for 循环判断到底放在低位链,还是高位链。


问题三:lastRun 到底十个什么?为什么不用在第二个循环里放?我不放的话,在哪里把 lastRun 后面的放到低位链或者高位链?


=============================================================================================================================




图片来自:https://blog.csdn.net/ZOKEKAI/article/details/90051567


在第一步中就有了答案,下面这两个代码直接把 lastRun 当做高位链或者低位链的开始,前面的再采用 for 循环慢慢往里插(头插法)


因为链表中的 node 有 next,lastRun 变成高位链或者低位链的时候待着后面的弟兄们就都过去了,后面再头插法不就连起来了?我真是个呆瓜。


if (runBit == 0) {//如果最后更新的 runBit,设置低位节点


ln = lastRun;


hn = null;


} else {//否则,设置高位节点


hn = lastRun;// 如果最后更新的 runBit 是 1, 设置高位节点


ln = null;


}


总结


================================================================


分为三步:


第一个 for 循环:找到 lastrun


两个 if:lastrun 节点变为高低位链的初始节点


第二个 for 循环:采用头插法往 lastRun 前面插,遍历到 lastRun 结束。


连接

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
ConcurrentHashMap扩容?lastRun到底是个啥?,理解透彻