1. 程式人生 > >jdk1.8 hashmap.putTreeVal方法解析

jdk1.8 hashmap.putTreeVal方法解析

final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                       int h, K k, V v) {
    Class<?> kc = null;//k的型別
    boolean searched = false;//是否遍歷過樹了
    TreeNode<K,V> root = (parent != null) ? root() : this;//樹的根節點
    for (TreeNode<K,V> p = root;;) {
        int dir, ph; K pk;//dir樹的左右方向,ph當前節點的hash,pk當前節點的key值
        if ((ph = p.hash) > h)
            dir = -1;
        else if (ph < h)
            dir = 1;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        //根據當前節點key值、hash和傳進來要插入的key值、hash一般都能得到一個左或右方向
        //或者當前節點key值、hash和傳進來要插入的key值、hash相等或equals
        //但是還有一種情況就是hash相等key不equals
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {
            //走到這裡說明:指定key沒有實現comparable介面
            //或者實現了comparable介面並且和當前節點的鍵物件比較之後相等
            /**searched 標識是否已經對比過當前節點的左右子節點了
			* 如果還沒有遍歷過,那麼就遞迴遍歷對比,看是否能夠得到那個鍵物件equals相等的節點
			* 如果得到了鍵的equals相等的的節點就返回
			* 如果還是沒有鍵的equals相等的節點,那說明應該建立一個新節點了
			* find(h, k, kc)遞迴、while遍歷ch的所有節點直到找到與k equals的節點否則就返回空
			*/
            if (!searched) {
                TreeNode<K,V> q, ch;
                searched = true;
                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;
            }
            // 走到這裡就說明,遍歷了所有子節點也沒有找到和當前鍵equals相等的節點
            dir = tieBreakOrder(k, pk);//把兩個key的object的hashcode()方法生成的hashcode比較決定方向
        }

        TreeNode<K,V> xp = p; // 定義xp指向當前節點
        /*
        * 如果dir小於等於0,那麼看當前節點的左節點是否為空,如果為空,就可以把要新增的元素作為當前節點的左節點,如果不為空,還需要下一輪繼續比較
        * 如果dir大於等於0,那麼看當前節點的右節點是否為空,如果為空,就可以把要新增的元素作為當前節點的右節點,如果不為空,還需要下一輪繼續比較
        * 如果以上兩條當中有一個子節點不為空,這個if中還做了一件事,那就是把p已經指向了對應的不為空的子節點,開始下一輪的比較
        */
        if ((p = (dir <= 0) ? p.left : p.right) == null) {  
            // 如果恰好要新增的方向上的子節點為空,此時節點p已經指向了這個空的子節點
            Node<K,V> xpn = xp.next; // 獲取當前節點的next節點
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn); // 建立一個新的樹節點
            if (dir <= 0)
                xp.left = x;  // 左孩子指向到這個新的樹節點
            else
                xp.right = x; // 右孩子指向到這個新的樹節點
            xp.next = x; // 連結串列中的next節點指向到這個新的樹節點
            x.parent = x.prev = xp; // 這個新的樹節點的父節點、前節點均設定為 當前的樹節點
            if (xpn != null) // 如果原來的next節點不為空
                ((TreeNode<K,V>)xpn).prev = x; // 那麼原來的next節點的前節點指向到新的樹節點
            moveRootToFront(tab, balanceInsertion(root, x));// 重新平衡,以及新的根節點置頂
            return null; // 返回空,意味著產生了一個新節點
        }
    }
}