HashMap 中 putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,int h, K k, V v)方法详解

200 阅读3分钟

1. putTreeVal

这个方法主要是,当hashMap put 一个新的元素时,发现它要插入的数组的索引位置上,已经是一个红黑树结构的节点时,就会调用这个方法对它进行插入或者寻找,如果插入成功返回null,否则返回跟当前插入元素k 相同的元素。

/**
 * map 当前节点所在的HashMap对象
 * tab 当前HashMap对象的元素数组
 * h   指定key的hash值
 * k   指定key
 * v   指定key上要写入的值
*/
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                               int h, K k, V v) {
   // 记录k的class类型                            
    Class<?> kc = null;
    // 标识符: 表示是否已经遍历过一次树
    boolean searched = false;
    // 返回当前节点的根节点
    TreeNode<K,V> root = (parent != null) ? root() : this;
    // 从根节点遍历,无终止条件,只能内部结束遍历
    for (TreeNode<K,V> p = root;;) {
        // dir : 插入的方向 -1 左,1 右;ph:节点p的hash值; pk:节点p的key
        int dir, ph; K pk;
        // 如果p的hash值大于要插入的节点的hash值,则dir = -1,表示该节点会被插入p的左边
        if ((ph = p.hash) > h)
            dir = -1;
        // 如果p的hash值小于要插入的节点的hash值,则dir = 1,表示该节点会被插入p的右边
        else if (ph < h)
            dir = 1;
        // 如果通过 == 比较或者 equals方法(可以重写)比较p和要插入节点的key 返回true
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            // 直接返回p,外层会判断当前插入的值,是否覆盖p的值
            return p;
        // 这里判断 (kc == null 且 k没有实现 Comparable 接口) 或者 compareComparables(kc, k, pk) == 0
        // 这两个方法详解 见下面3. 
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {
                 //走到这里说明:指定key没有实现comparable接口   或者   实现了comparable接口并且和当前节点的键对象比较之后相等
                 
            /*
             * searched 标识是否已经对比过当前节点的左右子节点了
             * 如果还没有遍历过,那么就递归遍历对比,看是否能够得到那个键对象equals相等的的节点
             * 如果得到了键的equals相等的的节点就返回
             * 如果还是没有键的equals相等的节点,那说明应该创建一个新节点了
             */ 
            // 判断是否已经遍历过,如果没有则进入
            if (!searched) {
                TreeNode<K,V> q, ch;
                // 把 searched 置为true
                searched = true;
                // 这里先从左子节点查询 如未找到在从右子节点找,最终返回 与指定key相等的节点
                if (((ch = p.left) != null &&
                     (q = ch.find(h, k, kc)) != null) ||
                    ((ch = p.right) != null &&
                     (q = ch.find(h, k, kc)) != null))
                    return q;
            }
            // 这个方法通常是前面比较都没相等 会调用它 详解见下方链接
            dir = tieBreakOrder(k, pk);
        }

        TreeNode<K,V> xp = p;
        // 当 dir <= 0时,表示要插入左边,把p.left赋给 p ,否则表示插入右边,把p.right 赋给p
        // 当p == null(这里是修改后的p) 时,说明此时左/右子节点为null 可以直接插入,否则还要继续遍历
        if ((p = (dir <= 0) ? p.left : p.right) == null) {
            // 这里是可以插入的情况
            // xpn : xp节点的next节点
            Node<K,V> xpn = xp.next;
            // 创建一个新的节点,并把它的next 设为 xpn
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
            // 如果 dir <= 0
            if (dir <= 0)
                // 把x设置为xp的左子节点
                xp.left = x;
            // 否则把x设置为 xp的右子节点
            else
                xp.right = x;
            // 把xp.next设为 x
            xp.next = x;
            // 把 x的前置节点和父节点都设为 xp
            x.parent = x.prev = xp;
            // 如果 xpn 不为 null 则把它的 前置节点设为x
            if (xpn != null)
                ((TreeNode<K,V>)xpn).prev = x;
            // moveRootToFront : 返回根节点,保证它在最前面
            // balanceInsertion: 平衡红黑树,使其满足红黑树的特性
            // 详解见下方链接
            moveRootToFront(tab, balanceInsertion(root, x));
            return null;
        }
    }
}
  1. tieBreakOrder--2.3
  2. moveRootToFront
  3. moveRootToFront
  4. balanceInsertion

2. root()

/**
 * 返回当前节点的根节点
 */
final TreeNode<K,V> root() {
    for (TreeNode<K,V> r = this, p;;) {
        if ((p = r.parent) == null)
            return r;
        r = p;
    }
}

3.comparableClassFor(k)

/**
 * 如果当前类未实现 Comparable 接口返回null 
 * Returns x's Class if it is of the form "class C implements
 * Comparable<C>", else null.
 */
static Class<?> comparableClassFor(Object x) {
    if (x instanceof Comparable) {
        Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
        if ((c = x.getClass()) == String.class) // 如果x是个字符串对象
            return c; // 返回String.class
        /*
         * 为什么如果x是个字符串就直接返回c了呢 ? 因为String  实现了 Comparable 接口
         */ 
 
        // 如果 c 不是字符串类,获取c直接实现的接口(如果是泛型接口则附带泛型信息)    
        if ((ts = c.getGenericInterfaces()) != null) {
            for (int i = 0; i < ts.length; ++i) { // 遍历接口数组
                // 如果当前接口t是个泛型接口 
                // 如果该泛型接口t的原始类型p 是 Comparable 接口
                // 如果该Comparable接口p只定义了一个泛型参数
                // 如果这一个泛型参数的类型就是c,那么返回c
                if (((t = ts[i]) instanceof ParameterizedType) &&
                    ((p = (ParameterizedType)t).getRawType() ==
                        Comparable.class) &&
                    (as = p.getActualTypeArguments()) != null &&
                    as.length == 1 && as[0] == c) // type arg is c 
                    return c;
            }
            // 上面for循环的目的就是为了看看x的class是否 implements  Comparable<x的class>
        }
    }
    return null; // 如果c并没有实现 Comparable<c> 那么返回空
}

4.compareComparables()

/**
 * Returns k.compareTo(x) if x matches kc (k's screened comparable
 * class), else 0.
 */
@SuppressWarnings({"rawtypes","unchecked"}) // for cast to Comparable
static int compareComparables(Class<?> kc, Object k, Object x) {
    // 这里 kc : 对应前面插入节点的key的类型, k : 对应前面插入节点的key ,X:当前遍历节点的key
    // 当 x == null或者 x.getClass() != kc 返回0,否则返回 (Comparable)k).compareTo(x)
    return (x == null || x.getClass() != kc ? 0 :
            ((Comparable)k).compareTo(x));
}