Java 基礎(chǔ)(八)手?jǐn)] ArrayList、HashMap、TreeMap

學(xué)了這么久的集合終于要結(jié)束了,在最后,我們手?jǐn)]幾個(gè)集合聊表對(duì)自己刻苦學(xué)習(xí)的尊敬吧~

本次一共選擇了三個(gè)具有代表性的集合作為模仿對(duì)象,分別是 ArrayList、HashMap、TreeMap。至于為什么選擇這三個(gè)集合,也是有原因的。因?yàn)锳rrayList、HashMap 是我們最常用到的集合,然后 TreeMap 是因?yàn)楸容^難以理解且底層數(shù)據(jù)結(jié)構(gòu)是鏈表(樹(shù)應(yīng)該也算是鏈表吧~)。

至于集合的三大體系之一的 Set 為什么沒(méi)選擇,我們?cè)趯W(xué)習(xí) Set 的時(shí)候已經(jīng)分析過(guò)了,這貨就是基于 map 做的實(shí)現(xiàn)。

好了,不多扯了,擼代碼吧~~

ArrayList

首先我們先來(lái)回顧一下List 接口

List 定義了一組元素是有序的、可重復(fù)的集合。

ArrayList 的特征

  • 容量不固定,可以動(dòng)態(tài)擴(kuò)容
  • 有序
  • 基于數(shù)組的實(shí)現(xiàn)

好,那么我們現(xiàn)在就來(lái)定義 CustomArrayList

在寫(xiě)代碼之前,我們先來(lái)明確一下要實(shí)現(xiàn)哪些功能,需要哪些屬性。

我們這里就只實(shí)現(xiàn)核心功能,需要實(shí)現(xiàn)的功能有~
1.實(shí)現(xiàn)增刪改查
2.動(dòng)態(tài)擴(kuò)容

需要的屬性有
1.用來(lái)保存實(shí)際元素的數(shù)組 elementData
2.用來(lái)記錄當(dāng)前保存的元素個(gè)數(shù) size

public class CustomArrayList<T> {

    private Object[] elementData;//存儲(chǔ)元素的數(shù)組
    private int size;//當(dāng)前存儲(chǔ)的元素個(gè)數(shù)

    public CustomArrayList() {
        elementData = new Object[10];//默認(rèn)10個(gè)長(zhǎng)度吧
        size = 0;//默認(rèn)0個(gè)元素
    }

    /**
     * 添加一個(gè)元素 t
     *
     * @param t 需要添加到 elementData 里面的元素
     */
    public void add(T t) {
        checkArrayLength(size + 1);
        elementData[size++] = t;
    }

    /**
     * 刪除角標(biāo) index 上的元素
     *
     * @param index 想要?jiǎng)h除的元素角標(biāo)
     * @return element 里面被刪除的元素
     */
    public T remove(int index) {
        checkIndex(index);

        T oldValue = (T) elementData[index];

        //刪除一個(gè)元素之后,需要把這個(gè)元素后面所有的元素前移一個(gè)角標(biāo)
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }

        elementData[--size] = null; // 清除多余的引用

        return oldValue;
    }

    /**
     * 獲取角標(biāo) index 上的元素
     *
     * @param index 想要獲取的元素角標(biāo)
     * @return element 對(duì)應(yīng)角標(biāo)的元素
     */
    public T get(int index) {
        checkIndex(index);
        return (T) elementData[index];
    }

    /**
     * 將元素 element 設(shè)置到 index 角標(biāo)上,并返回原來(lái)的元素
     *
     * @param index   想要修改的元素角標(biāo)
     * @param element 想要修改的的元素
     * @return 修改之前的元素
     */
    public T set(int index, T element) {
        checkIndex(index);
        T oldValue = (T) elementData[index];
        elementData[index] = element;
        return oldValue;
    }

    /**
     * 檢查是否包含元素 o
     *
     * @param o 想要查找的元素
     * @return true 表示包含元素o
     */
    public boolean contains(Object o) {
        for (Object t : elementData) {
            //o 為 null 則尋找 null 對(duì)象,o 不為 null 則 equals
            if (t == null && o == null || (t != null && t.equals(o)))
                return true;
        }

        return false;
    }

    /**
     * 檢查操作的角標(biāo)是否越界
     *
     * @param index 想要操作的元素角標(biāo)
     * @throws IndexOutOfBoundsException
     */
    private void checkIndex(int index) {
        if (index >= size) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        }
    }

    /**
     * 檢查數(shù)組長(zhǎng)度是否夠,如果不夠則執(zhí)行擴(kuò)容操作
     *
     * @param length 為當(dāng)前需要的數(shù)組長(zhǎng)度
     */
    private void checkArrayLength(int length) {
        if (length > elementData.length) {
            //需要擴(kuò)容
            grow(length);
        }
    }

    /**
     * 執(zhí)行擴(kuò)容操作
     *
     * @param minCapacity 當(dāng)前數(shù)組長(zhǎng)度
     */
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//擴(kuò)容1.5倍
        //這里不考慮擴(kuò)容超過(guò) Integer.MAX_VALUE 的情況
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

}

代碼量很少,都有注釋。一個(gè)精簡(jiǎn)版的 ArrayList 完成。

HashMap

照慣例先回顧一下 Map 接口。

Map: 是鍵值對(duì)關(guān)系的集合,并且鍵唯一,鍵一對(duì)一對(duì)應(yīng)值。

然后再回想一下 HashMap 的數(shù)據(jù)結(jié)構(gòu)。
首先是一個(gè)HashMapEntry<K,V>的數(shù)組 table,根據(jù)key.hashCode()&table.length 確定value 存放的 bucketIndex(桶)。然后每個(gè)bucketIndex 里面保存的是 key 的 hashCode 值相同的HashMapEntry 集,數(shù)據(jù)結(jié)構(gòu)是單向鏈表。

注意~這里有點(diǎn)意思,為什么是 key.hashCode()&(table.length-1),hashCode 我就不贅述了,介紹 HashMap 的時(shí)候我講過(guò),table.length 又是什么鬼呢,為什么要用這個(gè)數(shù)?

length 是數(shù)組的長(zhǎng)度,這點(diǎn)大家不會(huì)質(zhì)疑,所以數(shù)組的角標(biāo)只能是0~length-1,這里注意了,我們的 length 的擴(kuò)容機(jī)制決定了 length 的長(zhǎng)度只會(huì)是2的倍數(shù),所以這里的 length-1保證了值轉(zhuǎn)換成二進(jìn)制是 n 個(gè)1.然后能保證邏輯與上任何數(shù)都能小于 length。

再然后就是 key 沖突的問(wèn)題。hashcode 畢竟是隨機(jī)數(shù),很大概率會(huì)造成計(jì)算出來(lái)的bucketIndex 相等。上文也說(shuō)了,同一個(gè) bucketIndex 的 HashMapEntry 就是單向鏈表。鏈表的操作比較簡(jiǎn)單,我就不再多解釋了。

然后說(shuō)一下加載因子,由于鏈表比較長(zhǎng),入伙 HashMap 的所有元素全部保持在同一個(gè)bucketIndex 里面,即一條單向鏈表,那么 HashMap 就和 LinkedArrayList 沒(méi)什么區(qū)別了。所以需要引進(jìn)一個(gè)加載因子來(lái)加快查找效率,就是通過(guò)消耗更多的空間來(lái)降低查找時(shí)間。加載因子只能是0~1之間,默認(rèn)0.75 。加載因子是什么意思呢,就是 HashMap 里面元素的總個(gè)數(shù) > table.length 乘以 加載因子(符號(hào)沖突,乘號(hào)用漢字代替)的時(shí)候,就需要執(zhí)行table 的擴(kuò)容操作。

resize 時(shí)注意
最后需要注意的就是擴(kuò)容的時(shí)候,table.length-1 的值變了,之前 key.hashCode()&(table.length-1)的值有可能發(fā)生改變,所以需要重新計(jì)算 HashMap 中已經(jīng)存在的數(shù)據(jù)。因此,擴(kuò)容對(duì)于 HashMap 來(lái)說(shuō)是一項(xiàng)比較消耗性能的操作。

最后,在說(shuō)一下在 Java8中關(guān)于 HashMap 的優(yōu)化。理想情況下,HashMap 的查找時(shí)間復(fù)雜度是 O(1),但是在最差的情況(所有的 key 都保存在同一個(gè)bucketIndex里面),HashMap 的查找就是在一個(gè)單鏈表上查找內(nèi)容,時(shí)間復(fù)雜度是 O(n)。因此,針對(duì)這樣的情況,Java 對(duì) 超過(guò)8個(gè)元素的 bucketIndex鏈表自動(dòng)轉(zhuǎn)換成紅黑樹(shù)。所以在最糟糕的情況下,時(shí)間復(fù)雜度降低到了O(Logn)。

哦對(duì)了,還有 HashMapEntry ,這就是一個(gè) bean,存放了 hash、key、value、next.

好,理論分析完了,在開(kāi)始擼代碼之前,再次強(qiáng)調(diào)一下,HashMap 的數(shù)據(jù)結(jié)構(gòu)就是一個(gè)存放著鏈表表頭的數(shù)組。

public class CustomHashMap<K, V> {

    private MyEntry<K, V>[] table;//存放數(shù)據(jù)的 tab 表
    private int size;//當(dāng)前元素個(gè)數(shù)
    private int threshold;//當(dāng)前table 長(zhǎng)度最大存放的元素個(gè)數(shù),超過(guò)則需要擴(kuò)容

    public CustomHashMap() {
        table = new MyEntry[16];//默認(rèn)16個(gè)元素
        size = 0;
    }

    /**
     * 插入一組鍵值對(duì)
     *
     * @param key   插入的鍵值對(duì) key,可以為 null
     * @param value 插入的鍵值對(duì) value,可以為 null
     * @return 如果插入的 key 已存在,則返回被覆蓋之前的 oldValue,否則返回 null
     */
    public V put(K key, V value) {
        if (key == null) {//如果 key 為 null,則執(zhí)行插入空 kye 的方法
            return putForNullKey(value);
        }

        int hash = secondaryHash(key);
        int bucketIndex = indexFor(hash, table.length);
        for (MyEntry<K, V> e = table[bucketIndex]; 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;
                return oldValue;
            }
        }
        addEntry(hash, key, value, bucketIndex);
        return null;
    }

    /**
     * 刪除 key 所對(duì)應(yīng)的元素
     *
     * @param key 需要?jiǎng)h除元素的 key
     * @return 如果找到 key 對(duì)應(yīng)的元素value,則返回 value,否則返回 null
     */
    public V remove(Object key) {
        MyEntry<K, V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

    /**
     * 對(duì) key 為 null 的插入執(zhí)行特殊的處理。因?yàn)?key 為 null 無(wú)法進(jìn)行 equals 操作
     *
     * @param value 插入空 key 的 value
     * @return 如果插入的空 key 已存在,則返回被覆蓋之前的 oldValue,否則返回 null
     */
    private V putForNullKey(V value) {
        for (MyEntry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
        addEntry(0, null, value, 0);
        return null;
    }

    /**
     * 先檢測(cè)是否需要調(diào)整表的大小,然后再執(zhí)行創(chuàng)建 Entry 操作
     *
     * @param hash        插入 key 的 hash 值
     * @param key         需要插入元素 key
     * @param value       需要插入元素 value
     * @param bucketIndex 元素插入在 table 的索引
     */
    private void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? secondaryHash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }

    /**
     * 創(chuàng)建一個(gè)新的 MyEntry,并將其插入到表頭
     */
    private void createEntry(int hash, K key, V value, int bucketIndex) {
        MyEntry<K, V> e = table[bucketIndex];
        table[bucketIndex] = new MyEntry<>(hash, key, value, e);
        size++;
    }

    /**
     * 對(duì)當(dāng)前的 table 執(zhí)行擴(kuò)容操作
     * 這里默認(rèn)了加載因子為0.75
     * 注意:這里擴(kuò)容沒(méi)有做最大值限制,正常應(yīng)該做的
     *
     * @param newCapacity 需要擴(kuò)容的表長(zhǎng)
     */
    private void resize(int newCapacity) {
        MyEntry<K, V>[] newTable = new MyEntry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = newCapacity >> 1 + newCapacity >> 2;
    }

    /**
     * 將擴(kuò)容前舊表的所有元素轉(zhuǎn)移到新表
     * 這里消耗比較多,因?yàn)楸黹L(zhǎng)度變了,需要重新計(jì)算每個(gè)元素的 bucketIndex
     */
    private void transfer(MyEntry<K, V>[] newTable) {
        int newCapacity = newTable.length;
        for (MyEntry<K, V> e : table) {
            while (null != e) {
                MyEntry<K, V> next = e.next;
                int bucketIndex = indexFor(e.hash, newCapacity);
                e.next = newTable[bucketIndex];
                newTable[bucketIndex] = e;
                e = next;
            }
        }
    }

    /**
     * 查表刪除 key 對(duì)應(yīng)的元素
     * @return 如果有 key 的映射則返回映射的元素,如果沒(méi)有則返回 null
     *
     * */
    private MyEntry<K, V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        int hash = (key == null) ? 0 : secondaryHash(key);
        int bucketIndex = indexFor(hash, table.length);
        MyEntry<K, V> prev = table[bucketIndex];
        MyEntry<K, V> e = prev;

        while (e != null) {
            MyEntry<K, V> next = e.next;
            Object k;
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                size--;
                if (prev == e)
                    table[bucketIndex] = next;
                else
                    prev.next = next;
                return e;
            }
            prev = e;
            e = next;
        }

        return null;
    }

    /**
     * 從 api23 里面的 Collections 里面拷貝出來(lái)的計(jì)算 hash 方法
     * 我沒(méi)看懂,既然沒(méi)有用 Object.hashCode()肯定是這種算法比較優(yōu)吧
     *
     * @param obj 需要計(jì)算 hashCode 的對(duì)象
     */
    private int secondaryHash(Object obj) {
        int h = obj.hashCode();
        h += (h << 15) ^ 0xffffcd7d;
        h ^= (h >>> 10);
        h += (h << 3);
        h ^= (h >>> 6);
        h += (h << 2) + (h << 14);
        return h ^ (h >>> 16);
    }

    /**
     * 通過(guò) hashCode 計(jì)算 bucketIndex
     * @param h hashCode 值
     * @param length 表長(zhǎng)
     * @return 計(jì)算得出的 bucketIndex              
     * 
     * */
    private int indexFor(int h, int length) {
        return h & (length - 1);
    }

    /**
     * 保存 Key Value 的實(shí)體 bean
     * */
    private static class MyEntry<K, V> {
        private final K key;
        private V value;
        private MyEntry<K, V> next;
        private int hash;

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

    }

}

代碼到這里差不多就寫(xiě)完了,沒(méi)有對(duì)元素超過(guò)8個(gè)的 bucketIndex 進(jìn)行鏈表轉(zhuǎn)紅黑樹(shù)的操作,感興趣的小伙伴可自行實(shí)踐。
ps:檢查的時(shí)候發(fā)現(xiàn) get 方法忘記加了,大概和 remove 方法差不多,有強(qiáng)迫癥的小伙伴自己添加測(cè)試吧。性能上的話(huà),基本和原生的 HashMap 差不多。

TreeMap

TreeMap就是紅黑樹(shù),各種分析我在上一章已經(jīng)詳細(xì)講過(guò)了,這里怕大家忘了操作方法,我再?gòu)?fù)述一遍~

插入后修復(fù)邏輯

private void fixAfterInsertion(Node<K, V> e)
這個(gè)方法里面是個(gè) while 循環(huán),循環(huán)內(nèi)容就是以下3個(gè) case,case4是不用修復(fù)處理的。
循環(huán)條件是(e != null && e != root && isRedNode(e.parent))

  • case 1:當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)是紅色,叔叔節(jié)點(diǎn)(這個(gè)名詞能理解吧,不能理解我也沒(méi)辦法了)也是紅色。
    1. 將父節(jié)點(diǎn)設(shè)為黑色
    2. 將叔叔節(jié)點(diǎn)設(shè)為黑色
    3. 將祖父節(jié)點(diǎn)設(shè)為紅色
    4. 將祖父節(jié)點(diǎn)設(shè)為當(dāng)前節(jié)點(diǎn),重新判斷 case
  • case 2:當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)是紅色,叔叔節(jié)點(diǎn)是黑色,且當(dāng)前節(jié)點(diǎn)是父節(jié)點(diǎn)的右孩子
    1. 如果當(dāng)前節(jié)點(diǎn)是右孩子,將當(dāng)前節(jié)點(diǎn)指向父節(jié)點(diǎn),并自身左旋
    2. 設(shè)置父節(jié)點(diǎn)黑色,爺爺節(jié)點(diǎn)紅色
    3. 以爺爺節(jié)點(diǎn)為支點(diǎn)右旋,重新判斷 case
  • case 3:當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)是紅色,叔叔節(jié)點(diǎn)是黑色,且當(dāng)前節(jié)點(diǎn)是父節(jié)點(diǎn)的左孩子
    1. 如果當(dāng)前節(jié)點(diǎn)是左孩子,將當(dāng)前節(jié)點(diǎn)指向父節(jié)點(diǎn),并自身右旋
    2. 設(shè)置父節(jié)點(diǎn)黑色,爺爺節(jié)點(diǎn)紅色
    3. 以爺爺節(jié)點(diǎn)為支點(diǎn)左旋,重新判斷 case
  • case 4:啥?不是只有3種情況么?當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)是黑色
    1. 不用修復(fù),樹(shù)穩(wěn)定。

刪除節(jié)點(diǎn)邏輯

private void deleteEntry(Node<K, V> p)

  • case 1:要?jiǎng)h除的節(jié)點(diǎn)沒(méi)有孩子。如果要?jiǎng)h除的節(jié)點(diǎn)是黑色,則執(zhí)行刪除后修復(fù)操作。具體操作參見(jiàn)方法 deleteCase1(Node<K, V> p)
  • case 2:要?jiǎng)h除的節(jié)點(diǎn)有一個(gè)孩子。讓孩子指向父親即可,如果刪除的節(jié)點(diǎn)是黑色,執(zhí)行刪除后修復(fù)操作。具體操作參見(jiàn)方法 deleteCase2(Node<K, V> p)
  • case 3:要?jiǎng)h除的節(jié)點(diǎn)有兩個(gè)孩子。找到刪除節(jié)點(diǎn)右孩子的最左葉子節(jié)點(diǎn)或者左孩子的最右葉子節(jié)點(diǎn)。然后把葉子節(jié)點(diǎn)的 K、V賦值給需要?jiǎng)h除的節(jié)點(diǎn),再根據(jù)葉子節(jié)點(diǎn)是否有孩子執(zhí)行 case1或者 case2刪除即可。具體操作參見(jiàn)方法 deleteCase3(Node<K, V> p)

刪除后修復(fù)邏輯

private void fixAfterDeletion(Node<K, V> e)

這個(gè)方法里面是個(gè) while 循環(huán),循環(huán)內(nèi)容就是根據(jù)條件走4個(gè) step,循環(huán)條件是(e != root && !isRedNode(e))

這里真的沒(méi)看出什么名堂出來(lái),感覺(jué)就是不聽(tīng)的情況走不同的改變父節(jié)點(diǎn)和左右字節(jié)點(diǎn)的顏色以及旋轉(zhuǎn)。至于為什么要醬紫做,還是之前那句話(huà),你就當(dāng)這是數(shù)學(xué)家總結(jié)出來(lái)的規(guī)律!

源碼里面很多的 if else 嵌套操作,我這里拆分了成了四個(gè)step。就不用文字總結(jié) 每個(gè) step 該怎么操作了,反正代碼也很容易閱讀了。

擼代碼

這次的代碼沒(méi)有寫(xiě)什么注釋了,注釋都在上面分析了。代碼相對(duì)來(lái)說(shuō)也還是比較清晰,親測(cè)沒(méi)有毛病,理論上和 TreeMap 性能一樣(畢竟算法是一毛一樣的)。

public class CustomTreeMap<K extends Comparable, V> {

    enum Color {RED, BLACK}

    private Node<K, V> root = null;
    private int size = 0;


    public V put(K key, V value) {
        Node<K, V> t = root;
        if (t == null) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
            root = new Node<>(key, value, Color.BLACK);
            size = 1;
            return null;
        }

        int compare;
        Node<K, V> parent;
        do {
            parent = t;
            compare = key.compareTo(t.key);
            if (compare < 0)
                t = t.leftChild;
            else if (compare > 0)
                t = t.rightChild;
            else
                return t.setValue(value);
        } while (t != null);

        Node<K, V> e = new Node<>(key, value, Color.RED, parent);
        if (compare < 0)
            parent.leftChild = e;
        else
            parent.rightChild = e;
        fixAfterInsertion(e);
        size++;
        return null;
    }

    public V remove(K key) {
        Node<K, V> p = getEntry(key);
        if (p == null)
            return null;

        V oldValue = p.value;
        deleteEntry(p);
        return oldValue;
    }

    public V get(K key) {
        Node<K, V> p = getEntry(key);
        return (p == null ? null : p.value);
    }

    private Node<K, V> getEntry(K key) {
        if (key == null)
            throw new NullPointerException();
        Node<K, V> p = root;
        while (p != null) {
            int cmp = key.compareTo(p.key);
            if (cmp < 0)
                p = p.leftChild;
            else if (cmp > 0)
                p = p.rightChild;
            else
                return p;
        }
        return null;
    }


    private void deleteEntry(Node<K, V> p) {
        size--;
        if (p.leftChild != null && p.rightChild != null) {
            deleteCase3(p);
        } else if (p.leftChild != null || p.rightChild != null) {
            deleteCase2(p);
        } else {
            deleteCase1(p);
        }
    }

    //兩個(gè)孩子
    private void deleteCase3(Node<K, V> p) {
        Node<K, V> replaceNode;
        if (p.rightChild != null) {
            replaceNode = p.rightChild;
            while (replaceNode.leftChild != null)
                replaceNode = replaceNode.leftChild;
        } else {
            replaceNode = p.leftChild;
            while (replaceNode.rightChild != null)
                replaceNode = replaceNode.rightChild;
        }
        p.key = replaceNode.key;
        p.value = replaceNode.value;
        if (replaceNode.hasChild()) {
            deleteCase2(replaceNode);
        } else {
            deleteCase1(replaceNode);
        }
    }

    //一個(gè)孩子
    private void deleteCase2(Node<K, V> p) {
        Node<K, V> replacement = (p.leftChild != null ? p.leftChild : p.rightChild);
        replacement.parent = p.parent;
        if (p.parent == null)
            root = replacement;
        else if (p == p.parent.leftChild)
            p.parent.leftChild = replacement;
        else
            p.parent.rightChild = replacement;

        p.leftChild = p.rightChild = p.parent = null;

        if (p.color == Color.BLACK)
            fixAfterDeletion(replacement);
    }

    //沒(méi)有孩子
    private void deleteCase1(Node<K, V> p) {
        if (p.color == Color.BLACK)
            fixAfterDeletion(p);

        if (p.parent != null) {
            if (p == p.parent.leftChild)
                p.parent.leftChild = null;
            else if (p == p.parent.rightChild)
                p.parent.rightChild = null;
            p.parent = null;
        }
    }


    private void fixAfterDeletion(Node<K, V> e) {
        while (e != root && !isRedNode(e)) {
            Node<K, V> brother = brotherOf(e);
            if (brother != null && isRedNode(brother)) {
                fixDelStep1(brother);
            }
            fixDelStep2(e, brother);
        }
        setColor(e, Color.BLACK);
    }

    private void fixDelStep4(Node<K, V> brother, Node<K, V> e) {
        setColor(brother, colorOf(e.parent));
        setColor(e.parent, Color.BLACK);
        if (!isLeftChild(brother)) {
            setColor(brother.rightChild, Color.BLACK);
            rotateLeft(e.parent);
        } else {
            setColor(brother.leftChild, Color.BLACK);
            rotateRight(e.parent);
        }
        e = root;
    }

    private void fixDelStep3(Node<K, V> brother, Node<K, V> e) {
        setColor(brother, Color.RED);
        if (!isLeftChild(brother)) {
            setColor(brother.leftChild, Color.BLACK);
            rotateRight(brother);
            brother = e.parent.rightChild;
        } else {
            setColor(brother.rightChild, Color.BLACK);
            rotateLeft(brother);
            brother = e.parent.leftChild;
        }
    }

    private void fixDelStep2(Node<K, V> e, Node<K, V> brother) {
        if (!isRedNode(getLeftChild(brother)) &&
                !isRedNode(brother.rightChild)) {
            setColor(brother, Color.RED);
            e = e.parent;
        } else {
            if (!isLeftChild(brother) && !isRedNode(getRightChild(brother))
                    || isLeftChild(brother) && !isRedNode(getLeftChild(brother))) {
                fixDelStep3(brother, e);
            }
            fixDelStep4(brother, e);
        }
    }

    private void fixDelStep1(Node<K, V> brother) {
        setColor(brother, Color.BLACK);
        setColor(brother.parent, Color.RED);

        if (!isLeftChild(brother)) {
            rotateLeft(brother.parent);
            brother = brother.parent.rightChild;
        } else {
            rotateRight(brother.parent);
            brother = brother.parent.leftChild;
        }

    }

    private void fixAfterInsertion(Node<K, V> e) {
        while (e != null && e != root && isRedNode(e.parent)) {
            if (isRedNode(brotherOf(e.parent))) {
                insertCase1(e);
            } else if (!isRedNode(brotherOf(e.parent)) && !isLeftChild(e)) {
                insertCase2(e);
            } else if (isLeftChild(e)) {
                insertCase3(e);
            }
        }
        root.color = Color.BLACK;
    }

    private void insertCase3(Node<K, V> e) {
        if (isLeftChild(e)) {
            e = e.parent;
            rotateRight(e);
        }
        setColor(e.parent, Color.BLACK);
        setColor(e.parent.parent, Color.RED);
        rotateLeft(parentOf(parentOf(e)));
    }

    private void insertCase2(Node<K, V> e) {
        if (!isLeftChild(e)) {
            e = e.parent;
            rotateLeft(e);
        }
        setColor(e.parent, Color.BLACK);
        setColor(e.parent.parent, Color.RED);
        rotateRight(parentOf(parentOf(e)));
    }

    private void insertCase1(Node<K, V> e) {
        setColor(e.parent, Color.BLACK);
        setColor(brotherOf(e.parent), Color.BLACK);
        setColor(parentOf(parentOf(e)), Color.RED);
        e = e.parent.parent;
    }

    /**
     * From CLR
     */
    private void rotateLeft(Node<K, V> p) {
        if (p != null) {
            Node<K, V> r = p.rightChild;
            p.rightChild = r.leftChild;
            if (r.leftChild != null)
                r.leftChild.parent = p;
            r.parent = p.parent;
            if (p.parent == null)
                root = r;
            else if (p.parent.leftChild == p)
                p.parent.leftChild = r;
            else
                p.parent.rightChild = r;
            r.leftChild = p;
            p.parent = r;
        }
    }

    /**
     * From CLR
     */
    private void rotateRight(Node<K, V> p) {
        if (p != null) {
            Node<K, V> l = p.leftChild;
            p.leftChild = l.rightChild;
            if (l.rightChild != null) l.rightChild.parent = p;
            l.parent = p.parent;
            if (p.parent == null)
                root = l;
            else if (p.parent.rightChild == p)
                p.parent.rightChild = l;
            else p.parent.leftChild = l;
            l.rightChild = p;
            p.parent = l;
        }
    }

    private Color colorOf(Node<K, V> p) {
        return (p == null ? Color.BLACK : p.color);
    }

    private Node<K, V> brotherOf(Node<K, V> p) {
        return (p == null || p.parent == null ? null :
                isLeftChild(p) ? p.parent.rightChild : p.parent.leftChild);
    }

    private Node<K, V> parentOf(Node<K, V> p) {
        return p != null ? p.parent : null;
    }

    private boolean isLeftChild(Node<K, V> p) {
        return !(p == null || p.parent == null) && p.parent.leftChild == p;
    }

    private Node<K, V> getLeftChild(Node<K, V> p) {
        return p != null ? p.leftChild : null;
    }

    private Node<K, V> getRightChild(Node<K, V> p) {
        return p != null ? p.rightChild : null;
    }

    private boolean isRedNode(Node<K, V> p) {
        return p != null && p.color == Color.RED;
    }

    private void setColor(Node<K, V> p, Color c) {
        if (p != null)
            p.color = c;
    }

    private static class Node<K extends Comparable, V> {
        Color color = Color.BLACK;
        V value;
        K key;
        Node<K, V> parent, leftChild, rightChild;

        public Node(K key, V value, Color colorMe) {
            this.color = colorMe;
            this.value = value;
            this.key = key;
        }

        public Node(K key, V value, Color color, Node<K, V> parent) {
            this.value = value;
            this.key = key;
            this.color = color;
            this.parent = parent;
        }

        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        public boolean hasChild() {
            return leftChild != null || rightChild != null;
        }
    }
}

結(jié)束語(yǔ)

好了,集合的學(xué)習(xí)就此告一段落,強(qiáng)烈建議自己擼一遍這三個(gè)集合。ArrayList 的代碼量200不到,HashMap 和 TreeMap 的代碼量也都是在300行左右,這些都是集合的核心代碼,并且性能和官方源碼基本上是一樣的。掌握了在集合上就基本不會(huì)踩坑了,再次強(qiáng)調(diào),想要扎實(shí)掌握集合類(lèi),一定要自己手?jǐn)]一般。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容