JAVA學(xué)習(xí)筆記之HashMap

目錄

  1. 相關(guān)概念介紹
  2. 實現(xiàn)原理介紹
  3. 源碼分析
  4. 總結(jié)
  5. 參考地址

相關(guān)概念介紹

  • 數(shù)組
    采用一段連續(xù)的存儲單元來存儲數(shù)據(jù)抗果。
  • 線性鏈表
    具有鏈接存儲結(jié)構(gòu)的線性表兽埃,它用一組地址任意的存儲單元存放線性表中的數(shù)據(jù)元素捂襟,邏輯上相鄰的元素在物理上不要求也相鄰挺尿,不能隨機存取聋庵。一般用結(jié)點描述:結(jié)點(表示數(shù)據(jù)元素) =數(shù)據(jù)域(數(shù)據(jù)元素的映象) + 指針域(指示后繼元素存儲位置)
  • 紅黑樹
    紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹,在進行插入和刪除操作時通過特定操作保持二叉查找樹的平衡,從而獲得較高的查找性能腊凶。相關(guān)介紹參考紅黑樹原理和算法
  • 哈希表
    是根據(jù)關(guān)鍵碼值(Key value)而直接進行訪問的數(shù)據(jù)結(jié)構(gòu)划咐。也就是說,它通過把關(guān)鍵碼值映射到表中一個位置來訪問記錄钧萍,以加快查找的速度褐缠。這個映射函數(shù)叫做散列函數(shù),存放記錄的數(shù)組叫做散列表风瘦。
    給定表M队魏,存在函數(shù)f(key),對任意給定的關(guān)鍵字值key万搔,代入函數(shù)后若能得到包含該關(guān)鍵字的記錄在表中的地址胡桨,則稱表M為哈希(Hash)表俐载,函數(shù)f(key)為哈希(Hash) 函數(shù)。
  • 哈希沖突
    如果兩個不同的元素登失,通過哈希函數(shù)得出的實際存儲地址,然后要進行插入的時候挖炬,發(fā)現(xiàn)已經(jīng)被其他元素占用了揽浙,這就是所謂的哈希沖突擦酌,也叫哈希碰撞支竹。

實現(xiàn)原理介紹

HashMap原理圖

簡單來說,HashMap由數(shù)組+鏈表組成的格粪,數(shù)組是HashMap的主體草姻,鏈表則是主要為了解決哈希沖突而存在的钓猬,如果定位到的數(shù)組位置不含鏈表(當(dāng)前node的next指向null),那么對于查找,添加等操作很快撩独,僅需一次尋址即可敞曹;如果定位到的數(shù)組包含鏈表,對于添加操作综膀,其時間復(fù)雜度為O(n)澳迫,首先遍歷鏈表,存在即覆蓋剧劝,否則新增橄登;對于查找操作來講,仍需遍歷鏈表讥此,然后通過key對象的equals方法逐一比對查找拢锹。所以,性能考慮萄喳,HashMap中的鏈表出現(xiàn)越少卒稳,性能才會越好。

源碼分析

以下所有代碼基于jdk1.8

  • 成員變量
    //默認初始化容器大小 16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    //默認容器最大值 2^30
    static final int MAXIMUM_CAPACITY = 1 << 30;
    //默認的負載因子,當(dāng)容器元素數(shù)量達到總?cè)萘?DEFAULT_LOAD_FACTOR時會進行擴容操作
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //默認樹形閾值
    static final int TREEIFY_THRESHOLD = 8;
    //默認非樹形閾值
    static final int UNTREEIFY_THRESHOLD = 6;
    //默認紅黑樹最小容量
    static final int MIN_TREEIFY_CAPACITY = 64;
    //數(shù)組
    transient Node<K,V>[] table;
    //使用Set存儲所有的節(jié)點
    transient Set<Map.Entry<K,V>> entrySet;
    //map的大小
    transient int size;
    //hashMap修改的次數(shù)
    transient int modCount;
    //下一次擴容的閾值
    int threshold;
    //hash表的負載因子
    final float loadFactor;
  • 節(jié)點
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }
  • #put()操作
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
static final int hash(Object key) {
    int h;
    //h>>>16 無符號右移16位,hash的效果等于將key的hashCode的高16位^低16位運算
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //1.如果容器還未初始化,進行resize操作
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //2.n是2的倍數(shù)所以(以n=16為例)n-1為1111,(n-1)&hash就是取hash的低四位,即保證坐標值一定是在數(shù)組范圍之類
    //計算出該元素應(yīng)該放入數(shù)組的下標,這里表示當(dāng)該位置為null時,新增一個節(jié)點并放入
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
    //3.當(dāng)不為空時,默認采用開放尋址法尋找到key相同(或者新增)的節(jié)點
        Node<K,V> e; K k;
        //3.1 如果新增的key-value已經(jīng)有對應(yīng)的值了,不做操作,直接返回原值
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //3.2 如果數(shù)組中的節(jié)點是樹形節(jié)點,進行紅黑樹的插入操作
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
        //3.2 如果數(shù)組中的節(jié)點是線性鏈表,遍歷節(jié)點,如果有相同的就break,否則將節(jié)點加入到末尾取胎。
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //如果鏈表長度超過了樹形閾值,則將鏈表轉(zhuǎn)換成紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        //4. 如果e不為空展哭,說明找到相同key,替換新的value并返回舊的value
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            //自定義擴展方法闻蛀,LinkedHashMap中有實現(xiàn)
            afterNodeAccess(e);
            return oldValue;
        }
    }
    //5. 修改次數(shù)自增,容器大小自增,并且如果超過了閾值,進行resize操作
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

主要流程如下:

  • 判斷HashMap是否初始化匪傍,如果還沒有初始化,先初始化觉痛;
  • 通過hash&(n-1)算出在桶上的位置役衡,如果對應(yīng)位置為空,直接放入該位置中薪棒;
  • 如果桶上的對應(yīng)的位置不為空手蝎,則進入對應(yīng)的鏈表進行下一步判斷:
    • 根據(jù)hash或者key來判斷是否相同榕莺,相同時e=p;
    • 如果p是紅黑樹棵介,則進入紅黑樹的插入邏輯钉鸯,并返回e;
    • 遍歷p鏈表邮辽,根據(jù)hash或者key來判斷是否存在相同唠雕,如果存在直接返回e,否則創(chuàng)建新的節(jié)點吨述;
  • 根據(jù)上面返回的e節(jié)點來判斷岩睁,如果不為空,說明在HashMap中找到對應(yīng)的節(jié)點揣云,替換新的value值并返回舊值捕儒,結(jié)束put操作;
  • 修改容器大小邓夕,并判斷是否超過閾值刘莹,如果超過進行擴容操作。
  • #resize()操作
final Node<K,V>[] resize() {
    //1. 數(shù)據(jù)備份,數(shù)組,容量大小,擴容閾值
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    //2. 如果超過默認最大值,直接返回,否則變更大小(原大小*2)
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    //3. 如果原擴容閾值>0,新的容量=原擴容閾值,否則使用默認值
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    //4. 根據(jù)負載因子計算新的擴容閾值
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    //5. 根據(jù)新的容量創(chuàng)建新的tab
    @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    //6. 進行擴容操作
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                //5.1 對于單個節(jié)點,重新計算位置并放入
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                    //5.2 樹形節(jié)點單獨處理
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    //5.3 鏈表節(jié)點單獨處理
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        //將鏈表的數(shù)據(jù)分成兩波
                        //oldCap是舊桶的長度翎迁,是2的倍數(shù)栋猖,比如oldCap為16->10000
                        //e.hash&oldCap==0說明e.hash高位為0
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    //(e.hash & oldCap) == 0)的即hash值高位為0的還是原來的位置
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    //(e.hash & oldCap) != 0)的即hash值高位不為0的放入oldCap+j的位置
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

主要流程如下:

  • 先進行數(shù)組,容量大小,擴容閾值等的備份;

  • 擴容時如果是單節(jié)點汪榔,重新計算桶的位置蒲拉,新的桶位置根據(jù)hash值來,可能還在原來的位置痴腌,也可能翻倍增長雌团,如下圖中15->31;

  • 如果是紅黑樹節(jié)點,單獨處理士聪;

  • 如果是鏈表結(jié)構(gòu)锦援,將鏈表分為兩部分,一部分hash高位為0還保持原來的位置剥悟,另一部分放到數(shù)組原來位置+oldCap的位置上灵寺。如圖所示:


    鏈表擴容示意圖
  • 與1.7版本的比較
    1.7中沒有紅黑樹,所以代碼也比較簡單一點

public V put(K key, V value) {
    //如果數(shù)組為空,擴容
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    //根據(jù)找出的索引位置去判斷該位置上鏈表有沒有相同的entry
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    //增加entry
    addEntry(hash, key, value, i);
    return null;
}

void addEntry(int hash, K key, V value, int bucketIndex) {
    //判斷是否進行擴容操作
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
    //創(chuàng)建entry
    createEntry(hash, key, value, bucketIndex);
}

void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

 void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }

    Entry[] newTable = new Entry[newCapacity];
    //重點在這
    transfer(newTable, initHashSeedAsNeeded(newCapacity));
    table = newTable;
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

 void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);
            //多線程下會形成閉環(huán)
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

這里的擴容多線程情況下會出現(xiàn)閉環(huán)現(xiàn)象,下面通過幾張圖來解釋閉環(huán)的形成:
我們假設(shè) HashMap 從 2 resize到 4 :


初始圖

假設(shè)我們有兩個線程t1区岗,t2略板,假設(shè)t1Entry<K,V> next = e.next;處掛起,t2完成了后面的操作慈缔,在按照上面的代碼執(zhí)行后:


t1停止調(diào)度

這個時候t1又恢復(fù)了調(diào)度叮称,接著往下執(zhí)行:
t1恢復(fù)調(diào)度

接著往下執(zhí)行:
t1執(zhí)行1

t1執(zhí)行2

閉環(huán)形成:


閉環(huán)形成
  • #get()操作
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        //優(yōu)先檢查第一個節(jié)點
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            //如果是紅黑樹,進行紅黑樹操作
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}
  • #remove()操作
public V remove(Object key) {
    Node<K,V> e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}

final Node<K,V> removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
    Node<K,V>[] tab; Node<K,V> p; int n, index;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        Node<K,V> node = null, e; K k; V v;
        //找出需要remove的節(jié)點,跟get操作基本一致
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        else if ((e = p.next) != null) {
            if (p instanceof TreeNode)
                node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
            else {
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        //remove對應(yīng)的節(jié)點
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
            //紅黑樹對應(yīng)操作
            if (node instanceof TreeNode)
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
            //鏈表的對應(yīng)操作
            else if (node == p)
                tab[index] = node.next;
            else
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
            return node;
        }
    }
    return null;
}
  • 紅黑樹的實現(xiàn)
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent;  // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;
    TreeNode(int hash, K key, V val, Node<K,V> next) {
        super(hash, key, val, next);
    }
    //返回根節(jié)點
    final TreeNode<K,V> root() {
        for (TreeNode<K,V> r = this, p;;) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }

    /**
     * 由于TreeNode即是樹結(jié)構(gòu)也是雙向鏈表.所以這里
     * 保證樹的根節(jié)點同時也是鏈表的首節(jié)點
     */
    static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
        int n;
        if (root != null && tab != null && (n = tab.length) > 0) {
            int index = (n - 1) & root.hash;
            TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
            if (root != first) {
                Node<K,V> rn;
                tab[index] = root;
                TreeNode<K,V> rp = root.prev;
                if ((rn = root.next) != null)
                    ((TreeNode<K,V>)rn).prev = rp;
                if (rp != null)
                    rp.next = rn;
                if (first != null)
                    first.prev = root;
                root.next = first;
                root.prev = null;
            }
            assert checkInvariants(root);
        }
    }
    //尋找節(jié)點
    final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
        TreeNode<K,V> p = this;
        do {
            int ph, dir; K pk;
            TreeNode<K,V> pl = p.left, pr = p.right, q;
            //如果當(dāng)前節(jié)點的hash大于需要尋找節(jié)點的hash瓤檐,則指向其左孩子赂韵,否則指向右孩子,如果當(dāng)前節(jié)點就是要尋找的節(jié)點挠蛉,直接返回
            if ((ph = p.hash) > h)
                p = pl;
            else if (ph < h)
                p = pr;
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                return p;
            else if (pl == null)
                p = pr;
            else if (pr == null)
                p = pl;
            else if ((kc != null ||
                      (kc = comparableClassFor(k)) != null) &&
                     (dir = compareComparables(kc, k, pk)) != 0)
                p = (dir < 0) ? pl : pr;
            else if ((q = pr.find(h, k, kc)) != null)
                return q;
            else
                p = pl;
        } while (p != null);
        return null;
    }
    //獲取相應(yīng)的節(jié)點
    final TreeNode<K,V> getTreeNode(int h, Object k) {
        return ((parent != null) ? root() : this).find(h, k, null);
    }

    //插入操作
    final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,int h, K k, V v) {
        Class<?> kc = null;
        boolean searched = false;
        //獲取父節(jié)點
        TreeNode<K,V> root = (parent != null) ? root() : this;
        for (TreeNode<K,V> p = root;;) {
            int dir, ph; K pk;
            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;
            else if ((kc == null &&
                      (kc = comparableClassFor(k)) == null) ||
                     (dir = compareComparables(kc, k, pk)) == 0) {
                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;
                }
                dir = tieBreakOrder(k, pk);
            }

            TreeNode<K,V> xp = p;
            //根據(jù)hash判斷祭示,并且遍歷插入到葉子節(jié)點,再進行平衡調(diào)整
            if ((p = (dir <= 0) ? p.left : p.right) == null) {
                Node<K,V> xpn = xp.next;
                TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
                if (dir <= 0)
                    xp.left = x;
                else
                    xp.right = x;
                xp.next = x;
                x.parent = x.prev = xp;
                if (xpn != null)
                    ((TreeNode<K,V>)xpn).prev = x;
                    moveRootToFront(tab, balanceInsertion(root, x));
                    return null;
            }
        }
    }

    //刪除操作
    final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                              boolean movable) {
        int n;
        if (tab == null || (n = tab.length) == 0)
            return;
        int index = (n - 1) & hash;
        TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
        TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
        //1.先刪除鏈表的關(guān)系
        if (pred == null)
            tab[index] = first = succ;
        else
            pred.next = succ;
        if (succ != null)
            succ.prev = pred;
        if (first == null)
            return;
        //2.開始刪除樹形關(guān)系
        if (root.parent != null)
            root = root.root();
        //如果鏈表太小,從紅黑樹轉(zhuǎn)化成普通鏈表
        if (root == null || root.right == null ||
            (rl = root.left) == null || rl.left == null) {
            tab[index] = first.untreeify(map);  // too small
            return;
        }
        //2.1 被刪除節(jié)點左孩子與右孩子都不為空
        TreeNode<K,V> p = this, pl = left, pr = right, replacement;
        if (pl != null && pr != null) {
            TreeNode<K,V> s = pr, sl;
            //尋找刪除節(jié)點的后繼(中序遍歷)由于第一步已經(jīng)斷開本刪除節(jié)點與其后繼的鏈接谴古,所以這里使用中序遍歷找出其后繼
            while ((sl = s.left) != null) // find successor
                s = sl;
            //交換后繼與被刪除節(jié)點的顏色
            boolean c = s.red; s.red = p.red; p.red = c; // swap colors
            TreeNode<K,V> sr = s.right;
            TreeNode<K,V> pp = p.parent;
            //如果pr就是其后繼绍移,直接交換位置
            if (s == pr) { // p was s's direct parent
                p.parent = s;
                s.right = p;
            }
            else {
                TreeNode<K,V> sp = s.parent;
                if ((p.parent = sp) != null) {
                    if (s == sp.left)
                        sp.left = p;
                    else
                        sp.right = p;
                }
                if ((s.right = pr) != null)
                    pr.parent = s;
            }
            p.left = null;
            if ((p.right = sr) != null)
                sr.parent = p;
            if ((s.left = pl) != null)
                pl.parent = s;
            if ((s.parent = pp) == null)
                root = s;
            else if (p == pp.left)
                pp.left = s;
            else
                pp.right = s;
            //此時,被刪除的節(jié)點與其后繼位置交換完成
            if (sr != null)
                replacement = sr;
            else
                replacement = p;
        }
        else if (pl != null)
            //2.2 左子樹不為空
            replacement = pl;
        else if (pr != null)
            //2.3 右子樹不為空
            replacement = pr;
        else
            //2.4 左右子樹都為空
            replacement = p;
        //3. 左右子樹不為空進行讥电,使用非空子樹代替p
        if (replacement != p) {
            TreeNode<K,V> pp = replacement.parent = p.parent;
            if (pp == null)
                root = replacement;
            else if (p == pp.left)
                pp.left = replacement;
            else
                pp.right = replacement;
            p.left = p.right = p.parent = null;
        }
        //4. 當(dāng)刪除節(jié)點是黑色的時候進行平衡轉(zhuǎn)化
        TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
        //5. 點左右子樹都為空,直接刪除p節(jié)點
        if (replacement == p) {  // detach
            TreeNode<K,V> pp = p.parent;
            p.parent = null;
            if (pp != null) {
                if (p == pp.left)
                    pp.left = null;
                else if (p == pp.right)
                    pp.right = null;
            }
        }
        if (movable)
            moveRootToFront(tab, r);
    }

    //左旋(動手畫一下就懂了)
    static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                          TreeNode<K,V> p) {
        TreeNode<K,V> r, pp, rl;
        if (p != null && (r = p.right) != null) {
            if ((rl = p.right = r.left) != null)
                rl.parent = p;
            if ((pp = r.parent = p.parent) == null)
                (root = r).red = false;
            else if (pp.left == p)
                pp.left = r;
            else
                pp.right = r;
            r.left = p;
            p.parent = r;
        }
        return root;
    }
    //右旋(同理,動手畫一下)
    static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                           TreeNode<K,V> p) {
        TreeNode<K,V> l, pp, lr;
        if (p != null && (l = p.left) != null) {
            if ((lr = p.left = l.right) != null)
                lr.parent = p;
            if ((pp = l.parent = p.parent) == null)
                (root = l).red = false;
            else if (pp.right == p)
                pp.right = l;
            else
                pp.left = l;
            l.right = p;
            p.parent = l;
        }
        return root;
    }
    //插入平衡轉(zhuǎn)化
    static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                TreeNode<K,V> x) {
        //默認插入的節(jié)點為紅色
        x.red = true;
        for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
            //1.x為根節(jié)點,根節(jié)點默認為黑色,并直接返回
            if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            }
            else if (!xp.red || (xpp = xp.parent) == null)
            //2.父節(jié)點為黑色,或者祖父節(jié)點為空,即父節(jié)點是根節(jié)點,此時不需要調(diào)整
                return root;
            //3.分類討論,xp為左節(jié)點或者右節(jié)點
            if (xp == (xppl = xpp.left)) {
                //3.1. 再次分類,如果x的叔父節(jié)點:xppr,不為空且為紅色節(jié)點,此時先進行部分顏色調(diào)整
                if ((xppr = xpp.right) != null && xppr.red) {
                    //父節(jié)點,叔父節(jié)點變?yōu)楹谏?祖父變?yōu)榧t色,x變成祖父節(jié)點
                    xppr.red = false;
                    xp.red = false;
                    xpp.red = true;
                    x = xpp;
                }
                else {
                    //3.1.1. 再次分類,如果x為xp的右孩子,則對xp進行左旋
                    if (x == xp.right) {
                        root = rotateLeft(root, x = xp);
                        //重新對xp,xpp定義
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    //這里xp為原來的x為紅色,x也是紅色,所以先進行顏色調(diào)整,然后進行右旋
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateRight(root, xpp);
                        }
                    }
                }
            }
            else {
                //鏡像操作
                if (xppl != null && xppl.red) {
                    xppl.red = false;
                    xp.red = false;
                    xpp.red = true;
                    x = xpp;
                }
                else {
                    if (x == xp.left) {
                        root = rotateRight(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateLeft(root, xpp);
                        }
                    }
                }
            }
        }
    }
    //刪除平衡轉(zhuǎn)化
    static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                               TreeNode<K,V> x) {
        for (TreeNode<K,V> xp, xpl, xpr;;)  {
            if (x == null || x == root)
                return root;
            else if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            }
            else if (x.red) {
                //1.如果x的紅色節(jié)點,修改為黑色轧抗,無需調(diào)整結(jié)構(gòu)恩敌,直接返回
                x.red = false;
                return root;
            }
            else if ((xpl = xp.left) == x) {
                //2.x為左節(jié)點
                if ((xpr = xp.right) != null && xpr.red) {
                //如果x的叔父節(jié)點為紅色,此時左邊比右邊矮横媚,需要左旋
                    xpr.red = false;
                    xp.red = true;
                    root = rotateLeft(root, xp);
                    xpr = (xp = x.parent) == null ? null : xp.right;
                }
                //如果xpr為空,x指向xp
                if (xpr == null)
                    x = xp;
                else {
                //如果xpr不為空纠炮,則分別對xpr的左右孩子進行分類
                    TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                    if ((sr == null || !sr.red) &&
                        (sl == null || !sl.red)) {
                        //如果左孩子,右孩子滿足為空或者為黑色節(jié)點灯蝴,xpr轉(zhuǎn)為紅色恢口,x指向xp,進入下一次循環(huán)穷躁,xp會被轉(zhuǎn)成黑色耕肩,滿足紅黑樹條件
                        xpr.red = true;
                        x = xp;
                    }
                    else {
                        if (sr == null || !sr.red) {
                        //xpr左子樹不為空且為紅色
                            if (sl != null)
                            //xpr左子樹不為空,將其變成黑色
                                sl.red = false;                                                                                                             //xpr變成紅色问潭,不滿足紅黑樹3猿诸,4,需要進行右旋
                            xpr.red = true;
                            root = rotateRight(root, xpr);
                            xpr = (xp = x.parent) == null ?
                                null : xp.right;
                        }
                        if (xpr != null) {
                            xpr.red = (xp == null) ? false : xp.red;
                            if ((sr = xpr.right) != null)
                                sr.red = false;
                        }
                        if (xp != null) {
                            xp.red = false;
                            root = rotateLeft(root, xp);
                        }
                        x = root;
                    }
                }
            }
            else { // symmetric
                if (xpl != null && xpl.red) {
                    xpl.red = false;
                    xp.red = true;
                    root = rotateRight(root, xp);
                    xpl = (xp = x.parent) == null ? null : xp.left;
                }
                if (xpl == null)
                    x = xp;
                else {
                    TreeNode<K,V> sl = xpl.left, sr = xpl.right;
                    if ((sl == null || !sl.red) &&
                        (sr == null || !sr.red)) {
                        xpl.red = true;
                        x = xp;
                    }
                    else {
                        if (sl == null || !sl.red) {
                            if (sr != null)
                                sr.red = false;
                            xpl.red = true;
                            root = rotateLeft(root, xpl);
                            xpl = (xp = x.parent) == null ?
                                null : xp.left;
                        }
                        if (xpl != null) {
                            xpl.red = (xp == null) ? false : xp.red;
                            if ((sl = xpl.left) != null)
                                sl.red = false;
                        }
                        if (xp != null) {
                            xp.red = false;
                            root = rotateRight(root, xp);
                        }
                        x = root;
                    }
                }
            }
        }
    }
}

總結(jié)

參考地址

紅黑樹原理和算法
紅黑樹插入圖解
二叉樹的遍歷規(guī)則
紅黑樹化過程
推薦:并發(fā)情況下:Java HashMap 形成死循環(huán)的原因

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狡忙,一起剝皮案震驚了整個濱河市梳虽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灾茁,老刑警劉巖窜觉,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異北专,居然都是意外死亡禀挫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門逗余,熙熙樓的掌柜王于貴愁眉苦臉地迎上來特咆,“玉大人,你說我怎么就攤上這事∧甯瘢” “怎么了画拾?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長菜职。 經(jīng)常有香客問我青抛,道長,這世上最難降的妖魔是什么酬核? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任蜜另,我火速辦了婚禮,結(jié)果婚禮上嫡意,老公的妹妹穿的比我還像新娘举瑰。我一直安慰自己,他們只是感情好蔬螟,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布此迅。 她就那樣靜靜地躺著,像睡著了一般旧巾。 火紅的嫁衣襯著肌膚如雪耸序。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天鲁猩,我揣著相機與錄音坎怪,去河邊找鬼。 笑死廓握,一個胖子當(dāng)著我的面吹牛搅窿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播隙券,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼戈钢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了是尔?” 一聲冷哼從身側(cè)響起殉了,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拟枚,沒想到半個月后薪铜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡恩溅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年隔箍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脚乡。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜒滩,死狀恐怖滨达,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情俯艰,我是刑警寧澤捡遍,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站竹握,受9級特大地震影響画株,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜啦辐,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一谓传、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芹关,春花似錦续挟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浇冰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間聋亡,已是汗流浹背肘习。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坡倔,地道東北人漂佩。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像罪塔,于是被迫代替她去往敵國和親投蝉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容