JDK1.8hashmap源码中,两个地方提到了退化。
在扩容的split内如果节点个数 <= 6 个则将红黑树转为链表结构
//对lo表(表1)进行判断 if (loHead != null) { // 原索引位置的节点不为空 // 如果节点个数lc<=6个则将红黑树转为链表结构 if (lc <= UNTREEIFY_THRESHOLD)//lo表内满足退化条件 tab[index] = loHead.untreeify(map);//将对应索引index的树变为链表 else { // 将原索引位置的节点设置为对应的头节点 tab[index] = loHead; //如果hiHead不为空,则代表原来的红黑树(老表的红黑树由于节点被分到两个位置) // 已经被改变, 需要重新构建新的红黑树 if (hiHead != null) //以loHead为根节点, 构建新的红黑树 loHead.treeify(tab);//使用链表lo变树 } } //下面同上,对hi表(表2)进行判断 if (hiHead != null) {// 索引位置为原索引+oldCap的节点不为空 // 如果节点个数<=6个则将红黑树转为链表结构 if (hc <= UNTREEIFY_THRESHOLD) tab[index + bit] = hiHead.untreeify(map); else { //将索引位置为原索引+oldCap的节点设置为对应的头节点 tab[index + bit] = hiHead; // loHead不为空则代表原来的红黑树(老表的红黑树由于节点被分到两个位置) // 已经被改变, 需要重新构建新的红黑树 if (loHead != null) //以hiHead为根节点, 构建新的红黑树 hiHead.treeify(tab); } }
由源码可见,退化条件使用了lc <= UNTREEIFY_THRESHOLD(6)。我暂且死记硬背将它记下,例如:当树的节点个数小于等于6个时需要进行树的退化。对吗??难说
删除树节点函数中满足四个非空条件任意一个的树型退化。(与6无关)
// 通过root节点来判断此红黑树是否太小, 如果是则调用untreeify方法转为链表节点并返回 if (root == null || root.right == null || (rl = root.left) == null || rl.left == null) { tab[index] = first.untreeify(map); // too small return; } //使用四个非空判断,其中一个不满足非空,就需要退化,根据刚才背的退化条件:当树的节点小于等于6时需要退化。 //那么显而易见,删除一个节点后,树的结点个数明显变为了6个,需要退化。是这样的吗???
我们将满足这个判断条件的树列出观察:
//第一种树形,凑合着看吧哈哈`
//第二种树形,只包含这两种情况`
//注意第三种树形右边这颗树`
最后举了个栗子
由此可见,在树节点的删除中,退化的判断条件并非节点个数小于等于6,而是这四个非空条件所涵盖的树的形状。至于节点个数,0-10个内都有可能退化( (3)(4)中的树可证 )。
写到最后:退化这一点是我跟伙伴们在用C语言实现JDK1.8的hashmap时遇到的一些疑问与思考。还存在很多考虑不周,表达不清,甚至是知识盲区。学习过之后,简单记录一下。仅供参考。
如果有错,大家尽管指出,反正不会改!哈哈。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)