JDK1.8 Collection知識點與代碼分析--TreeMap

本期系列文章的其他文章

JDK1.8 Collection知識點與代碼分析–整體框架
JDK1.8 Collection知識點與代碼分析–ArrayList&LinkedList
JDK1.8 Collection知識點與代碼分析–HashMap
JDK1.8 Collection知識點與代碼分析–LinkedHashMap
JDK1.8 Collection知識點與代碼分析–HashSet&TreeSet

這篇文章接着打開TreeMap的源碼進行分析. 首先TreeMap是util工具類中一個非常好用的有序映射, 自身是非線程安全的, 相比HashMap, 增加了對NavigableMap, sortedMap接口的實現, 增加了一系列基於鍵有序的api, 這裏先列舉幾個比較常用的
sortedMap

  • firstKey(): 返回最小的key
  • lastKey(): 返回最大的key
  • subMap(from, to): 返回的是map的view, 左閉右開
  • headMap(toKey, inclusive): 返回比toKey嚴格小(inclusive爲true時包括)的這部分map的view
  • tailMap(fromKey, inclusive): 返回比fromKey嚴格大(inclusive爲true時包括)的這部分map的view

NavigableMap(繼承SortedMap)

  • ceilingEntry(key)/ ceilingKey(key): 返回大於等於key參數的最小的元素, 如果沒有這樣的元素存在返回null
  • floorEntry(key)/floorKey(key): 返回小於等於key參數的最大元素, 如果沒有這樣的元素, 返回null
  • higherEntry/higherKey: 和ceiling類似, 只不過嚴格大
  • lowerEntry/lowerKey: 和floor類似, 只不過嚴格小

TreeMap的核心數據結構是紅黑樹, 其紅黑樹的實現和HashMap中的紅黑樹實現幾乎是一模一樣的, 只不過函數名和獲取父節點, 左右節點的方法有微小的差別, 以下貼出核心部分的代碼.
fixAfterInsertion這個方法和HashMap中的balanceInsertion是一模一樣的, 下面的註釋看完沒有弄懂的可以看下這篇講hashMap的文章中的balanceInsertion部分, 我在註釋中通過圖解的方式, 對每個部分解決的問題進行了梳理.

private void fixAfterInsertion(Entry<K,V> x) {
   x.color = RED;

    while (x != null && x != root && x.parent.color == RED) {
        // x不是root節點, x的父節點爲紅節點
        // x的父節點是祖先節點的左兒子
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
            Entry<K,V> y = rightOf(parentOf(parentOf(x)));
            if (colorOf(y) == RED) {
                // flip Color
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);
                x = parentOf(parentOf(x));
            } else {

                if (x == rightOf(parentOf(x))) {
                    // x是右節點, x和父節點是紅節點, 所以先左旋
                    x = parentOf(x);
                    rotateLeft(x);
                }
                // x, 父節點是左兒子, 也都是紅節點, 右旋
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateRight(parentOf(parentOf(x)));
            }
        } else {
            //父節點是右兒子
            Entry<K,V> y = leftOf(parentOf(parentOf(x)));
            if (colorOf(y) == RED) {
                //flip
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);
                x = parentOf(parentOf(x));
            } else {
                // 和左兒子對稱
                if (x == leftOf(parentOf(x))) {
                    x = parentOf(x);
                    rotateRight(x);
                }
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateLeft(parentOf(parentOf(x)));
            }
        }
    }
    root.color = BLACK;
}

TreeMap中一個需要注意的細節, 當它沒有指定comparator時, 是不允許null作爲key的, 使用comparator時, 取決於comparator是否允許key爲null.

對於NavigatableMap的api中的方法, 這裏只給出一個例子, 它們的思路都非常相似

final Entry<K,V> getHigherEntry(K key) {
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        if (cmp < 0) {
            // 如果當前節點比key大, 就繼續往左找, 看是否能找到比當前更小的比key大的元素
            if (p.left != null)
                p = p.left;
            else
                return p;
        } else {
            if (p.right != null) {
                p = p.right;
            } else {
                // 如果更大的沒有了, 往父節點找, 找到第一個比key大的父節點(回溯,找到第一個向左走的位置)
                Entry<K,V> parent = p.parent;
                Entry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        }
    }
    return null;
}

這個方法的返回值並不是直接返回的, 而是通過exportEntry方法, 構造了一個SimpleImmutableEntry的實例進行發佈, 保證安全性.

而對於SortedMap的api中方法, 它們返回的都是TreeMap的靜態內部類AscendingSubMap, 這個靜態內部類又是繼承於TreeMap的抽象靜態內部類NavigableSubMap, 這個類的成員變量如下:


/**
         * The backing map.
         */
        final TreeMap<K,V> m;

        /**
         * Endpoints are represented as triples (fromStart, lo,
         * loInclusive) and (toEnd, hi, hiInclusive). If fromStart is
         * true, then the low (absolute) bound is the start of the
         * backing map, and the other values are ignored. Otherwise,
         * if loInclusive is true, lo is the inclusive bound, else lo
         * is the exclusive bound. Similarly for the upper bound.
         */
        final K lo, hi;
        final boolean fromStart, toEnd;
        final boolean loInclusive, hiInclusive;

可以看到, 它是成員TreeMap的一個view, 內部維護了lo,hi兩個key, 它的ceiling, floor等方法, 都是在檢查是否在lo, hi範圍後, 調用TreeMapgetCeilingEntry等方法.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章