Java集合:TreeMap使用詳解及源碼分析

1 使用方法

  TreeMap和HashMap一樣是散列表,但是他們內部實現完全不同,TreeMap基於紅黑樹實現,是一個有序的散列表,而HashMap使用數組加鏈表實現是無序的。

public class TreeMap<K,V>
        extends AbstractMap<K,V>
        implements NavigableMap<K,V>, Cloneable, java.io.Serializable {}

  TreeMap繼承了AbstractMap,儲的是key-value鍵值對;
  TreeMap實現了NavigableMap接口,支持多種導航方法,可以精準的獲得鍵值對;
  TreeMap和HashMap一樣實現了Cloneable和Serializable接口,可以複製和序列化。

1.1 方法介紹

  TreeMap提供的API主要有如下:

Entry<K, V>                ceilingEntry(K key) //返回鍵不小於key的最小鍵值對entry
K                          ceilingKey(K key) //返回鍵不小於key的最小鍵
void                       clear() //清空TreeMap
Object                     clone() //克隆TreeMap
Comparator<? super K>      comparator() //比較器
boolean                    containsKey(Object key) //是否包含鍵爲key的鍵值對
NavigableSet<K>            descendingKeySet() //獲取降序排列key的Set集合
NavigableMap<K, V>         descendingMap() //獲取降序排列的Map
Set<Entry<K, V>>           entrySet() //獲取鍵值對entry的Set集合
Entry<K, V>                firstEntry() //第一個entry
K                          firstKey() //第一個key
Entry<K, V>                floorEntry(K key) //獲取不大於key的最大鍵值對
K                          floorKey(K key) //獲取不大於key的最大Key
V                          get(Object key) //獲取鍵爲key的值value
NavigableMap<K, V>         headMap(K to, boolean inclusive) //獲取從第一個節點開始到to的子Map, inclusive表示是否包含to節點
SortedMap<K, V>            headMap(K toExclusive) //獲取從第一個節點開始到to的子Map, 不包括toExclusive
Entry<K, V>                higherEntry(K key) //獲取鍵大於key的最小鍵值對
K                          higherKey(K key) //獲取鍵大於key的最小鍵
boolean                    isEmpty() //判空
Set<K>                     keySet() //獲取key的Set集合
Entry<K, V>                lastEntry() //最後一個鍵值對
K                          lastKey() //最後一個鍵
Entry<K, V>                lowerEntry(K key) //鍵小於key的最大鍵值對
K                          lowerKey(K key) //鍵小於key的最大鍵值對
NavigableSet<K>            navigableKeySet() //返回key的Set集合
Entry<K, V>                pollFirstEntry() //獲取第一個節點,並刪除
Entry<K, V>                pollLastEntry() //獲取最後一個節點並刪除
V                          put(K key, V value) //插入一個節點
V                          remove(Object key) //刪除鍵爲key的節點
int                        size() //Map大小
SortedMap<K, V>            subMap(K fromInclusive, K toExclusive) //獲取從fromInclusive到toExclusive子Map,前閉後開
NavigableMap<K, V>         subMap(K from, boolean fromInclusive, K to, boolean toInclusive)
NavigableMap<K, V>         tailMap(K from, boolean inclusive) //獲取從from開始到最後的子Map,inclusive標誌是否包含from
SortedMap<K, V>            tailMap(K fromInclusive)

1.2 使用示例

public void testTreeMap() {
    //新建treeMap
    TreeMap treeMap = new TreeMap();
    //添加元素
    treeMap.put(11, "eleven");
    treeMap.put(1, "one");
    treeMap.put(2, "two");
    treeMap.put(3, "three");
    treeMap.put(4, "four");
    //打印元素
    this.printMapByEntrySet(treeMap);
    //獲取大小
    System.out.println("treeMap的大小爲: " + treeMap.size());
    //是否包含key爲4的元素
    System.out.println("treeMap是否包含key爲4的元素: " + treeMap.containsKey(4));
    //是否包含值爲5的元素
    System.out.println("treeMap是否包含value爲two的元素: " + treeMap.containsValue("two"));

    treeMap.put(5, "five");
    treeMap.put(6, "six");
    treeMap.put(9, "nine");
    treeMap.put(11, "eleven");

    //獲取treeMap中鍵不小於8最小的entry
    System.out.println("treeMap中鍵不小於8的最小entry爲: " + treeMap.ceilingEntry(8));
    //獲取第一個entry
    System.out.println("treeMap中第一個entry爲: " + treeMap.firstEntry());
    //獲取從from開始到to結束的子map,前閉後開
    System.out.println("從2開始到9結束的子map爲: " + treeMap.subMap(2,9));
    //刪除元素
    System.out.println("刪除key爲2的元素: " + treeMap.remove(2));
    //獲取並刪除最後一個元素
    System.out.println("獲取並刪除最後一個元素" + treeMap.pollLastEntry());
    //打印元素
    this.printMapByKeySet(treeMap);
    //clone
    TreeMap cloneMap = (TreeMap) treeMap.clone();
    //打印克隆map
    System.out.println("cloneMap的元素爲: " + cloneMap);
    //清空map
    treeMap.clear();
    //判空
    System.out.println("treeMap是否爲空: " + treeMap.isEmpty());
}

/**
 * 根據entrySet()獲取Entry集合,然後遍歷Set集合獲取鍵值對
 * @param map
 */
private void printMapByEntrySet(TreeMap map) {
    Integer key = null;
    String value = null;
    Iterator iterator = map.entrySet().iterator(); //
    System.out.print("treeMap中含有的元素有: ");
    while (iterator.hasNext()) {
        Map.Entry entry = (Map.Entry) iterator.next();
        key = (Integer) entry.getKey();
        value = (String) entry.getValue();
        System.out.print("key/value : " + key + "/" + value + " ");
    }
    System.out.println();
}

/**
 * 使用keySet獲取key的Set集合,利用key獲取值
 * @param map
 */
private void printMapByKeySet(TreeMap map) {
    Integer key = null;
    String value = null;
    Iterator iterator = map.keySet().iterator();
    System.out.print("treeMap中含有的元素有: ");
    while (iterator.hasNext()) {
        key = (Integer) iterator.next();
        value = (String) map.get(key);
        System.out.print("key/value : " + key + "/" + value + " ");
    }
    System.out.println();
}

  運行結果如下:

treeMap中含有的元素有: key/value : 1/one key/value : 2/two key/value : 3/three key/value : 4/four key/value : 11/eleven
treeMap的大小爲: 5
treeMap是否包含key爲4的元素: true
treeMap是否包含valuetwo的元素: true
treeMap中鍵不小於8的最小entry爲: 9=nine
treeMap中第一個entry爲: 1=one2開始到9結束的子map爲: {2=two, 3=three, 4=four, 5=five, 6=six}
刪除key爲2的元素: two
獲取並刪除最後一個元素11=eleven
treeMap中含有的元素有: key/value : 1/one key/value : 3/three key/value : 4/four key/value : 5/five key/value : 6/six key/value : 9/nine
cloneMap的元素爲: {1=one, 3=three, 4=four, 5=five, 6=six, 9=nine}
treeMap是否爲空: true

2 源碼分析

2.1構造函數

TreeMap有四個構造函數,這四個構造函數的區別在於使用什麼樣的構造器,以及是否要初始化,源碼中有註釋解釋。

/**
 * Constructs a new, empty tree map, using the natural ordering of its
 * keys.  All keys inserted into the map must implement the {@link
 * Comparable} interface.  Furthermore, all such keys must be
 * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
 * a {@code ClassCastException} for any keys {@code k1} and
 * {@code k2} in the map.  If the user attempts to put a key into the
 * map that violates this constraint (for example, the user attempts to
 * put a string key into a map whose keys are integers), the
 * {@code put(Object key, Object value)} call will throw a
 * {@code ClassCastException}.
 */
public TreeMap() {
    comparator = null;
}

/**
 * Constructs a new, empty tree map, ordered according to the given
 * comparator.  All keys inserted into the map must be <em>mutually
 * comparable</em> by the given comparator: {@code comparator.compare(k1,
 * k2)} must not throw a {@code ClassCastException} for any keys
 * {@code k1} and {@code k2} in the map.  If the user attempts to put
 * a key into the map that violates this constraint, the {@code put(Object
 * key, Object value)} call will throw a
 * {@code ClassCastException}.
 *
 * @param comparator the comparator that will be used to order this map.
 *        If {@code null}, the {@linkplain Comparable natural
 *        ordering} of the keys will be used.
 */
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

/**
 * Constructs a new tree map containing the same mappings as the given
 * map, ordered according to the <em>natural ordering</em> of its keys.
 * All keys inserted into the new map must implement the {@link
 * Comparable} interface.  Furthermore, all such keys must be
 * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
 * a {@code ClassCastException} for any keys {@code k1} and
 * {@code k2} in the map.  This method runs in n*log(n) time.
 *
 * @param  m the map whose mappings are to be placed in this map
 * @throws ClassCastException if the keys in m are not {@link Comparable},
 *         or are not mutually comparable
 * @throws NullPointerException if the specified map is null
 */
public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}

/**
 * Constructs a new tree map containing the same mappings and
 * using the same ordering as the specified sorted map.  This
 * method runs in linear time.
 *
 * @param  m the sorted map whose mappings are to be placed in this map,
 *         and whose comparator is to be used to sort this map
 * @throws NullPointerException if the specified map is null
 */
public TreeMap(SortedMap<K, ? extends V> m) {
    comparator = m.comparator();
    try {
        buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
    } catch (java.io.IOException cannotHappen) {
    } catch (ClassNotFoundException cannotHappen) {
    }
}
###2.2 put方法
/*
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
        * @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
*
        * @return the previous value associated with {@code key}, or
*         {@code null} if there was no mapping for {@code key}.
        *         (A {@code null} return can also indicate that the map
*         previously associated {@code null} with {@code key}.)
        * @throws ClassCastException if the specified key cannot be compared
*         with the keys currently in the map
* @throws NullPointerException if the specified key is null
        *         and this map uses natural ordering, or its comparator
*         does not permit null keys
*/
}
public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) { //空樹,插入根節點
        compare(key, key); // type (and possibly null) check

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent; //父節點
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    if (cpr != null) { //如果有自定義的比較器則使用自定義比較器比較key
        do {
            parent = t;
            cmp = cpr.compare(key, t.key); //首先比較父節點
            if (cmp < 0) //比父節點小,則比較左孩子
                t = t.left;
            else if (cmp > 0) //比父節點大比較則比較右孩子
                t = t.right;
            else
                return t.setValue(value); //鍵相同則替換原value
        } while (t != null); //t==null時則找到要插入的節點
    }
    else {
        if (key == null) //同上
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    Entry<K,V> e = new Entry<>(key, value, parent); //插入的節點
    if (cmp < 0) //比較爲小於0,則將新節點設爲上一個t的左孩子,反之右孩子
        parent.left = e;
    else
        parent.right = e;
    fixAfterInsertion(e); //恢復紅黑數的特性
    size++;
    modCount++;
    return null;
}

2.3 get方法

/**
 * Returns the value to which the specified key is mapped,
 * or {@code null} if this map contains no mapping for the key.
 *
 * <p>More formally, if this map contains a mapping from a key
 * {@code k} to a value {@code v} such that {@code key} compares
 * equal to {@code k} according to the map's ordering, then this
 * method returns {@code v}; otherwise it returns {@code null}.
 * (There can be at most one such mapping.)
 *
 * <p>A return value of {@code null} does not <em>necessarily</em>
 * indicate that the map contains no mapping for the key; it's also
 * possible that the map explicitly maps the key to {@code null}.
 * The {@link #containsKey containsKey} operation may be used to
 * distinguish these two cases.
 *
 * @throws ClassCastException if the specified key cannot be compared
 *         with the keys currently in the map
 * @throws NullPointerException if the specified key is null
 *         and this map uses natural ordering, or its comparator
 *         does not permit null keys
 */
public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

final Entry<K,V> getEntry(Object key) {
    // Offload comparator-based version for sake of performance
    if (comparator != null)
        return getEntryUsingComparator(key); //有比較器,大部分情況下都是沒有比較器的,所以拆出來
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
    Comparable<? super K> k = (Comparable<? super K>) key;
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = k.compareTo(p.key);
        if (cmp < 0)
            p = p.left;
        else if (cmp > 0)
            p = p.right;
        else
            return p;
    }
    return null;
}

/**
 * Version of getEntry using comparator. Split off from getEntry
 * for performance. (This is not worth doing for most methods,
 * that are less dependent on comparator performance, but is
 * worthwhile here.)
 */
final Entry<K,V> getEntryUsingComparator(Object key) {
    @SuppressWarnings("unchecked")
    K k = (K) key;
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        Entry<K,V> p = root;
        while (p != null) {
            int cmp = cpr.compare(k, p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
    }
    return null;
}

2.4 remove方法

/**
 * Removes the mapping for this key from this TreeMap if present.
 *
 * @param  key key for which mapping should be removed
 * @return the previous value associated with {@code key}, or
 *         {@code null} if there was no mapping for {@code key}.
 *         (A {@code null} return can also indicate that the map
 *         previously associated {@code null} with {@code key}.)
 * @throws ClassCastException if the specified key cannot be compared
 *         with the keys currently in the map
 * @throws NullPointerException if the specified key is null
 *         and this map uses natural ordering, or its comparator
 *         does not permit null keys
 */
public V remove(Object key) {
    Entry<K,V> p = getEntry(key); //獲取節點
    if (p == null)
        return null;

    V oldValue = p.value;
    deleteEntry(p); //刪除節點
    return oldValue;
}

/**
 * Delete node p, and then rebalance the tree.
 */
private void deleteEntry(Entry<K,V> p) {
    modCount++;
    size--;

    // If strictly internal, copy successor's element to p and then make p
    // point to successor.
    if (p.left != null && p.right != null) { //有左右孩子, 則將後繼節點的值複製給父節點,然後處理他的後繼節點
        Entry<K,V> s = successor(p); //獲取後繼節點
        p.key = s.key;
        p.value = s.value;
        p = s;
    } // p has 2 children

    // Start fixup at replacement node, if it exists.
    Entry<K,V> replacement = (p.left != null ? p.left : p.right);

    if (replacement != null) { //後繼節點有子節點
        // Link replacement to parent
        replacement.parent = p.parent; //將 後繼節點的子節點的父節點 設置爲後繼節點的父節點
        if (p.parent == null) //後繼節點爲根節點
            root = replacement;
        else if (p == p.parent.left)
            p.parent.left  = replacement;
        else
            p.parent.right = replacement;

        // Null out links so they are OK to use by fixAfterDeletion.
        p.left = p.right = p.parent = null; //刪除掉後繼節點, help GC

        // Fix replacement
        if (p.color == BLACK) //如果後繼節點的顏色爲黑色
            //根據紅黑樹的特性"從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點", 刪除的黑節點,會破壞平衡性
            fixAfterDeletion(replacement); //重新染色, 平衡的紅黑樹
    } else if (p.parent == null) { // return if we are the only node.
        root = null;
    } else { //  No children. Use self as phantom replacement and unlink.
        if (p.color == BLACK)
            fixAfterDeletion(p);

        if (p.parent != null) {
            if (p == p.parent.left)
                p.parent.left = null;
            else if (p == p.parent.right)
                p.parent.right = null;
            p.parent = null;
        }
    }
}

/**
 * 紅黑樹的後繼節點爲
 * 1 如果有右孩子, 則爲右孩子的最深左孩子
 * 2 如果沒有右孩子, 則爲最淺的以t爲右子樹節點的節點
 * Returns the successor of the specified Entry, or null if no such.
 */
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
    if (t == null)
        return null;
    else if (t.right != null) {
        Entry<K,V> p = t.right;
        while (p.left != null)
            p = p.left;
        return p;
    } else {
        Entry<K,V> p = t.parent;
        Entry<K,V> ch = t;
        while (p != null && ch == p.right) {
            ch = p;
            p = p.parent;
        }
        return p;
    }
}

參考:

[1] http://www.cnblogs.com/skywang12345/p/3310928.html

[2] http://blog.csdn.net/ns_code/article/details/36421085

發佈了47 篇原創文章 · 獲贊 31 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章