今天分享一下对HashMap中treeify方法的简单理解:
该方法实现了TreeNode类对象,将该对象的打头链表转换为树结构,小伙伴们都知道,HashMap的底层是由数组+链表+红黑树,而1.8的HashMap的一大特点便是新增了红黑树。基础参考**—>**浅读一
老规矩,怎么理解知识点,当然是直接上代码跟着我的注释理思路:
final void treeify(Node<K,V>[] tab) {//Node<K,V>[] 定义了参数数组
TreeNode<K,V> root = null;//这里小伙伴应该可以理解吧-->创建了树的根节点root
for (TreeNode<K,V> x = this, next; x != null; x = next) {//这里的话可能会有模糊,不着急一点一点的看,这里的for是不是将链表遍历了呢,而x则是指向当前节点,next则是指向下一节点
next = (TreeNode<K,V>)x.next;//指向下一个节点
x.left = x.right = null;//这里就比较简单了,就是将当前节点的左右节点都定义为null
if (root == null) {//这里就用到了第二行根节点root,如果根节点为null
x.parent = null;//那就设定当前节点的父节点为空
x.red = false;//red 红色 false 否定 这里是将当前节点的红色属性定义为false 也就是说当前节点为黑色节点
root = x;//并将当前节点赋给根节点
}//上述做个总结:当前节点的父节点为null,那么当前节点便是父节点,同时将其定义为黑节点
else {//else 那就是说 接下来的操作是基于 root父节点不为空,也就是说当前节点x不是父节点而是子节点的情况
K k = x.key;//key是什么 hashmap是不是<K,V>键值对格式?答案是肯定的 这里是拿到当前链表节点的Key值
int h = x.hash;//那这里就是拿到当前链表节点的Hash值了
Class<?> kc = null;//这里是先定义一个key所属的Class
for (TreeNode<K,V> p = root;;) {//for 又开始遍历了 ***注意***:这里没有定义上限边界,也就是说只能在内部跳出,下面我们会看到 **注意:** p 是树节点
int dir, ph;//单看这里只是定义了两个int对象 遗留:ph ,dir 接着往下看
K pk = p.key;//p.key 当前树节点的key被拿到
if ((ph = p.hash) > h)//当前节点的hash付给了ph 那也就是说ph是当前树节点的hash值 而h是链节点的hash值 整合: 如果当前树节点hash值大于当前链节点的hash值
dir = -1;// ph已经知道 dir是什么 不慌 接着往下看
else if (ph < h)//如果当前树节点hash小于当前链节点的hash
dir = 1;//不慌 再看
else if ((kc == null &&//如果两个节点的key的hash值相等
(kc = comparableClassFor(k)) == null) //如果当前链表节点的key实现了comparable接口,并且当前树节点和链表节点是相同Class的实例
|| (dir = compareComparables(kc, k, pk)) == 0)//通过comparable的方式再比较两者
dir = tieBreakOrder(k, pk);//最后再通过tieBreakOrder比较一次
//到这里应该明白了 dir表示的是方向左右 不清楚的 可以代入再走一遍思维
TreeNode<K,V> xp = p;//这里是保存了当前的树节点
//如果dir小于等于0 那么表示当前链表节点是在树节点的左侧,但不一定是该树节点的左子节点,也可能是左子节点的右子节点,或者循环往下更深层次 到这里迷糊不清楚的 可以学习一下树结构
//如果dir大于零 那么当前链节点在当前树节点的右侧,但不一定是当前树节点的右子节点,同上
//如果当前树节点不是叶子节点,那么最终会以当前树节点的左子节点或者右子节点 为起始节点 再从GOTO1 处开始 重新寻找自己(当前链表节点)的位置
//如果当前树节点就是叶子节点,那么根据dir的值,就可以把当前链表节点放置到当前树节点的左或者右侧了。
//放置之后,还需要重新把树进行平衡,平衡之后,就可以针对下一个链表节点进行处理
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;//当前链表节点 作为 当前树节点的子节点
if (dir <= 0)
xp.left = x;// 作为左子节点
else
xp.right = x;// 作为右子节点
root = balanceInsertion(root, x);// 用balanceInsertion方法重新进行平衡
break;//跳出循环
}
}
}
}
moveRootToFront(tab, root);
}
到此为止,便是将打头链表转为红黑树的方法整理了一遍,如果记不住treeify方法的话,就记住树化方法,名如其意。