HashMap中紅黑樹TreeNode的split方法源碼解讀

PS:由於文檔是我在本地編寫好之後再複製過來的,有些文本格式沒能完整的體現,故提供下述圖片,供大家閱覽,以便有更好的閱讀體驗:
在這裏插入圖片描述
在這裏插入圖片描述
HashMap中紅黑樹TreeNode的split方法源碼解讀
分析HashMap$TreeNode(既是樹又是鏈表)的split方法的源碼,會發現主要分兩部分操作:

  1. 數據從舊數組轉移到新數組上來時,舊數組上的數據會根據(e.hash & oldCap) 是否等於0,重新rehash計算其在新數組上的索引位置,分成2類:
    ① 等於0時,則將該樹鏈表頭節點放到新數組時的索引位置等於其在舊數組時的索引位置,記未低位區樹鏈表lo開頭-low;
    ② 不等於0時,則將該樹鏈表頭節點放到新數組時的索引位置等於其在舊數組時的索引位置再加上舊數組長度,記爲高位區樹鏈表hi開頭high(具體推導過程,詳見《HashMap擴容時的rehash方法中(e.hash & oldCap) == 0源碼解讀》).

  2. 當紅黑樹被split分割開成爲兩個小紅黑樹後:
    ① 當低位區小紅黑樹元素個數小於等於6時,開始去樹化untreeify操作;
    ② 當低位區小紅黑樹元素個數大於6且高位區紅黑樹不爲null時,開始樹化操作(賦予紅黑樹的特性)
    具體,詳見下述的源碼解析:
    1./HashMap$TreeNode的split()方法/
    方法概述:將紅黑樹從舊數組轉移到新數組
    /
    *
    * Splits nodes in a tree bin into lower and upper tree bins,
    * or untreeifies if now too small. Called only from resize;
    * see above discussion about split bits and indices.
    *
    * @param map the map
    * @param tab the table for recording bin heads
    * @param index the index of the table being split
    * @param bit the bit of hash to split on(其實就是舊數據的容量大小oldCap)
    */
    final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
    TreeNode<K,V> b = this;
    // Relink into lo and hi lists, preserving order
    TreeNode<K,V> loHead = null, loTail = null;
    TreeNode<K,V> hiHead = null, hiTail = null;
    int lc = 0, hc = 0;
    for (TreeNode<K,V> e = b, next; e != null; e = next) {
    next = (TreeNode<K,V>)e.next;
    e.next = null;
    if ((e.hash & bit) == 0) {//區分高低位樹鏈表的核心算法
    if ((e.prev = loTail) == null) //低位尾部標記爲null,表示還未開始處理,此時e是第一個要處理的低位樹鏈表節點,故e.prev等於loTail都等於null.
    loHead = e; //低位樹鏈表的第一個樹鏈表節點
    else
    loTail.next = e;
    loTail = e;
    ++lc; //低位樹元素個數計數
    }
    else {
    if ((e.prev = hiTail) == null)
    hiHead = e; //高位樹鏈表的第一個樹鏈表節點
    else
    hiTail.next = e;
    hiTail = e;
    ++hc; //高位樹鏈表元素個數計數
    }
    }

        if (loHead != null) {//低位樹鏈表不爲null
            if (lc <= UNTREEIFY_THRESHOLD) //低位樹鏈表元素個數若小於等於6
                tab[index] = loHead.untreeify(map); //開始去樹化操作(就是將元素ThreeNode節點 類型都替換成Node節點類型),具體方法源碼詳見第2點
            else {
                tab[index] = loHead;
                if (hiHead != null) // (else is already treeified) //若高位樹鏈表頭節點爲空,說明還未處理完高位,還不能開始樹化操作
                    loHead.treeify(tab); //低位樹鏈表元素個數若大於6且高位樹鏈表頭節點不等於null,開始將低位樹鏈表真正樹化成紅黑樹(前面都只是掛着TreeNode的名號,但實際只是鏈表結構,還沒包含紅黑樹的特性,在這裏才賦予了它紅黑樹的特性) 具體方法源碼詳見[《HashMap之鏈表轉紅黑樹(樹化 )-treefyBin方法源碼解讀》](https://editor.csdn.net/md/?articleId=106801839)
    

}
}
if (hiHead != null) { //原理同低位樹鏈表處理,不再贅述
if (hc <= UNTREEIFY_THRESHOLD)
tab[index + bit] = hiHead.untreeify(map);
else {
tab[index + bit] = hiHead;
if (loHead != null)
hiHead.treeify(tab);
}
}
}
2. /HashMap$TreeNode的untreeify ()方法/
方法概述:將鏈表的TreeNode類型轉換成Node節點類型
/
*
* Returns a list of non-TreeNodes replacing those linked from
* this node.
*/
final Node<K,V> untreeify(HashMap<K,V> map) {
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = this; q != null; q = q.next) {
Node<K,V> p = map.replacementNode(q, null); //將節點的類型轉換成Node鏈表節點類型, 具體方法源碼詳見第3點

            if (tl == null)
                hd = p;
            else
                tl.next = p;
            tl = p;
        }
        return hd;
    }
  1. /*HashMap的replacementNode ()方法/
    方法概述:將節點p的類型轉換成Node鏈表節點類型
    // For conversion from TreeNodes to plain nodes
    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
    return new Node<>(p.hash, p.key, p.value, next); //創建Node類型節點並返回
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章