TreeMap實現原理及源碼分析之JDK8


轉載 Java 集合系列12之 TreeMap詳細介紹(源碼解析)和使用示例

一、TreeMap 簡單介紹

什么是Map?

  在數組中我們通過數組下標來對數組內容進行索引的,而在Map中我們通過對象來對 對象進行索引,用來索引的對象叫做key,其對應的對象叫做value。這就是我們平時說的鍵值對。

什么是TreeMap?

  TreeMap是一個有序的key-value集合,是非線程安全的,基於紅黑樹(Red-Black tree)實現。其映射根據鍵的自然順序進行排序,或者根據創建映射時提供的 Comparator 進行排序,具體取決於使用的構造方法。其基本操作 containsKey、get、put 和 remove 的時間復雜度是 log(n) 。TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。

  當自定義比較器時,需要自定義類實現java.lang.Comparable接口,並重寫compareTo()方法。

TreeMap與HashMap的區別:

  • 數據結構不同:
    • HashMap是基於哈希表,由 數組+鏈表+紅黑樹 構成。
    • TreeMap是基於紅黑樹實現。
  • 存儲方式不同:
    • HashMap是通過key的hashcode對其內容進行快速查找。
    • TreeMap中所有的元素都保持着某種固定的順序。
  • 排列順序:
    • HashMap存儲順序不固定。
    • TreeMap存儲順序固定,可以得到一個有序的結果集。

二、TreeMap源碼分析

 1.TreeMap類繼承圖:

TreeMap類定義:

public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable
  • TreeMap<K,V>:TreeMap是以key-value形式存儲數據的。
  • extends AbstractMap<K,V>:繼承了AbstractMap,大大減少了實現Map接口時需要的工作量。
  • implements NavigableMap<K,V>:實現了SortedMap,支持一系列的導航方法。比如返回有序的key集合。
  • implements Cloneable:表明其可以調用克隆方法clone()來返回實例的field-for-field拷貝。
  • implements Serializable:表明該類是可以序列化的。

2.類成員變量和靜態內部類Entry:

// 比較器對象
private final Comparator<? super K> comparator;

// 根節點
private transient Entry<K,V> root;

// 集合大小
private transient int size = 0;

// 樹結構被修改的次數
private transient int modCount = 0;

 // 紅黑樹節點顏色
 private static final boolean RED = false;
 private static final boolean BLACK = true;

// 靜態內部類用來表示節點類型
static final class Entry<K,V> implements Map.Entry<K,V> {
    K key;     //
    V value;   //
    Entry<K,V> left;    // 指向左子樹的引用(指針)
    Entry<K,V> right;   // 指向右子樹的引用(指針)
    Entry<K,V> parent;  // 指向父節點的引用(指針)
    boolean color = BLACK; 
}

關鍵字transient的作用:

  transient是Java語言的關鍵字,它被用來表示一個域中不是該對象串行化的一部分。
  Java的 serialization 提供了一種持久化對象實例的機制。當持久化對象時,可能有一個特殊的對象數據成員,我們不想用serialization機制來保存它。為了在一個特定對象的一個域上關閉serialization,可以在這個域前加上關鍵字transient。
當一個對象被串行化的時候,transient型的變量值 不包括在串行化的表示中,而 非transient型的變量 是被包括進去的。

3.TreeMap的構造函數:

// 默認構造函數。使用默認比較器比較key的大小,TreeMap中的元素按照自然排序進行排列。
public TreeMap() {
   // 默認比較機制 comparator
= null; } // 帶比較器的構造函數 public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } // 帶Map的構造函數,Map會成為TreeMap的子集 public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); } // 帶SortedMap的構造函數,SortedMap會成為TreeMap的子集 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) { } }

putAll(Map<? extends K, ? extends V> map) 方法:

/**
 * Map中的所有元素添加到TreeMap中
 */
public void putAll(Map<? extends K, ? extends V> map) {
    int mapSize = map.size();
    // TreeMap的size為0,map的size不為0,並且map是SortedMap的實例
    if (size==0 && mapSize!=0 && map instanceof SortedMap) {
        Comparator<?> c = ((SortedMap<?,?>)map).comparator();
        // 默認比較器等於map的比較器,或者map的比較器不為null,並且與默認比較器相等
        if (c == comparator || (c != null && c.equals(comparator))) {
            ++modCount;
            try {
                buildFromSorted(mapSize, map.entrySet().iterator(),
                                null, null);
            } catch (java.io.IOException cannotHappen) {
            } catch (ClassNotFoundException cannotHappen) {
            }
            return;
        }
    }
    // 使用父類putAll()
    super.putAll(map);
}

/**
 * Map中的所有元素添加到TreeMap中
 */
public void putAll(Map<? extends K, ? extends V> m) {
    // 遍歷map一個一個添加到TreeMap中
    for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
        put(e.getKey(), e.getValue());
}

buildFromSorted(int size, Iterator<?> it, java.io.ObjectInputStream str, V defaultVal) 方法:

/**
 * SortedMap(有序的map)中的所有元素添加到TreeMap中
 */
private void buildFromSorted(int size, Iterator<?> it,
                             java.io.ObjectInputStream str,
                             V defaultVal)
    throws  java.io.IOException, ClassNotFoundException {
    this.size = size;
    root = buildFromSorted(0, 0, size-1, computeRedLevel(size),
                           it, str, defaultVal);
}

/**
 * 將map中的元素逐個添加到TreeMap中,並返回map的中間元素作為根節點
 */
private final Entry<K,V> buildFromSorted(int level, int lo, int hi,
                                         int redLevel,
                                         Iterator<?> it,
                                         java.io.ObjectInputStream str,
                                         V defaultVal)
    throws  java.io.IOException, ClassNotFoundException {

    if (hi < lo) return null;

    // 獲取中間元素
    int mid = (lo + hi) >>> 1;

    Entry<K,V> left  = null;
    
    // 若lo小於mid,則遞歸調用獲取(middel的)左孩子。
    if (lo < mid)
        left = buildFromSorted(level+1, lo, mid - 1, redLevel,
                               it, str, defaultVal);

    // 從Iterator或stream中獲取middle節點對應的key和value
    K key;
    V value;
    if (it != null) {
        if (defaultVal==null) {
            Map.Entry<?,?> entry = (Map.Entry<?,?>)it.next();
            key = (K)entry.getKey();
            value = (V)entry.getValue();
        } else {
            key = (K)it.next();
            value = defaultVal;
        }
    } else { // use stream
        key = (K) str.readObject();
        value = (defaultVal != null ? defaultVal : (V) str.readObject());
    }

    // 創建middle節點
    Entry<K,V> middle =  new Entry<>(key, value, null);

    // 若當前節點的深度==紅色節點的深度,則將節點着色為紅色
    if (level == redLevel)
        middle.color = RED;

    // 設置middle為left的父親,left為middle的左孩子
    if (left != null) {
        middle.left = left;
        left.parent = middle;
    }

    if (mid < hi) {
        // 遞歸調用獲取(middel的)右孩子
        Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,
                                           it, str, defaultVal);
        // 設置middle為right的父親,right為middle的右孩子
        middle.right = right;
        right.parent = middle;
    }

    return middle;
}

  添加到紅黑樹中時,只將level == redLevel的節點設為紅色。表示第level級節點,實際上是用buildFromSorted方法轉換成紅黑樹后 的最底端的節點(假設根節點在最上方);只將紅黑樹最底端的級別 着色為紅色,其余都是黑色。

4.核心方法:

紅黑樹相關的方法:

rotateLeft(Entry<K,V> p) 方法:

/**
 * 左旋
 */
private void rotateLeft(Entry<K,V> p) {
    if (p != null) {
        
        Entry<K,V> r = p.right; // 令p節點右孩子為r節點
        p.right = r.left; // 令r節點的左孩子為 p節點的右孩子
        if (r.left != null)
            r.left.parent = p; // 當r節點的左孩子不為null時,令p節點為 r節點的左孩子 的父節點
        r.parent = p.parent; // 令p節點的父節點為 r節點的父節點
        if (p.parent == null)
            root = r; // 當p節點的父節點為null時,令r節點為根節點
        else if (p.parent.left == p)
            p.parent.left = r; // 當p節點的父節點的左孩子為p節點時,令r節點為 p節點的父節點的左孩子
        else
            p.parent.right = r; // 當p節點的父節點的右孩子為p節點時,令r節點為 p節點的父節點的右孩子
        r.left = p; // 令p節點為 r節點的左孩子
        p.parent = r; // 令r節點為 p節點的父節點
    }
}

put(K key, V value) 方法:

/**
 * 插入操作
 */
public V put(K key, V value) {
    Entry<K,V> t = root; // 獲取根節點
    if (t == null) {
        compare(key, key); // 檢查key的類型,是否為null

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    
    // 獲取比較器
    Comparator<? super K> cpr = comparator;
    // 比較器不為null時,即自定義了比較器
    if (cpr != null) {
        // 循環比較插入節點的key與根節點的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); // 插入節點與根節點的key的大小相同,直接覆蓋
        } while (t != null);
    }
    // 比較器為null時,使用默認的比較器
    else {
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        // 循環比較插入節點的key與根節點的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)
        parent.left = e;
    else
        parent.right = e;
    // 插入修正操作,使插入節點后,TreeMap還是紅黑樹結構
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}

與紅黑樹有關的方法還有:

  • 右旋-rotateRight(Entry<K,V> p)
  • 插入修正為紅黑樹-fixAfterInsertion(Entry<K,V> x)
  • 刪除-deleteEntry(Entry<K,V> p)
  • 刪除修正為紅黑樹-fixAfterDeletion(Entry<K,V> x)

都是根據算法翻譯成代碼,具體可參考這里。

TreeMap中Entry相關的方法:

  TreeMap的 firstEntry()、 lastEntry()、 lowerEntry()、 higherEntry()、 floorEntry()、 ceilingEntry()、 pollFirstEntry() 、 pollLastEntry() 原理類似,以下講解firstEntry()方法。

firstEntry() 方法:

/**
 * 獲取第一個節點
 */
public Map.Entry<K,V> firstEntry() {
    return exportEntry(getFirstEntry());
}

/**
 * 獲取第一個節點
 */
final Entry<K,V> getFirstEntry() {
    // 獲取根節點
    Entry<K,V> p = root; 
    if (p != null)
        while (p.left != null)
            p = p.left;
    return p;
}

/**
 * 獲取第一個節點
 */
static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) {
    // 如果節點為null,創建AbstractMap.SimpleImmutableEntry類型的對象,並返回
    return (e == null) ? null :
        new AbstractMap.SimpleImmutableEntry<>(e);
}
public static class SimpleImmutableEntry<K,V>
    implements Entry<K,V>, java.io.Serializable
{
    private static final long serialVersionUID = 7138329143949025153L;

    private final K key;
    private final V value;

    public SimpleImmutableEntry(K key, V value) {
        this.key   = key;
        this.value = value;
    }

    public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {
        this.key   = entry.getKey();
        this.value = entry.getValue();
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public V setValue(V value) {
        throw new UnsupportedOperationException();
    }

    public boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> e = (Map.Entry<?,?>)o;
        return eq(key, e.getKey()) && eq(value, e.getValue());
    }

    public int hashCode() {
        return (key   == null ? 0 :   key.hashCode()) ^
               (value == null ? 0 : value.hashCode());
    }

    public String toString() {
        return key + "=" + value;
    }

}
SimpleImmutableEntry 類的實現

  從上面,我們可以看出 firstEntry() 和 getFirstEntry() 都是用於獲取第一個節點。但是,firstEntry() 是對外接口; getFirstEntry() 是內部接口。而且,firstEntry() 是通過 getFirstEntry() 來實現的。那為什么外界不能直接調用 getFirstEntry(),而需要多此一舉的調用 firstEntry() 呢?

  這么做的目的是:防止用戶修改返回的Entry。getFirstEntry()返回的Entry是可以被修改的,但是經過firstEntry()返回的Entry不能被修改,只可以讀取Entry的key值和value值。因為exportEntry()方法所在類 SimpleImmutableEntry 的 setValue()方法會拋出 UnsupportedOperationException() 異常。

TreeMap中key相關方法:

  TreeMap的firstKey()、lastKey()、lowerKey()、higherKey()、floorKey()、ceilingKey()原理都是類似的,下面以ceilingKey()來進行詳細說明。

ceilingKey(K key) 方法:

/**
 * 獲取大於/等於key的最小節點所對應的key,沒有的話返回null
 */
public K ceilingKey(K key) {
    return keyOrNull(getCeilingEntry(key));
}
/**
 * 尋找大於/等於key的最小節點
 */
final Entry<K,V> getCeilingEntry(K key) {
    Entry<K,V> p = root;
    while (p != null) {
        int cmp = compare(key, p.key);
        // 如果根節點的key大於給定key
        if (cmp < 0) {
            if (p.left != null)
                p = p.left;
            else
                return p;
        } else if (cmp > 0) { // 如果根節點的key小於給定key
            if (p.right != null) {
                p = p.right;
            } else { // 如果根節點的右孩子為null
                Entry<K,V> parent = p.parent;
                Entry<K,V> ch = p;
                while (parent != null && ch == parent.right) {
                    ch = parent;
                    parent = parent.parent;
                }
                return parent;
            }
        } else
            return p;
    }
    return null;
}
/**
 * 如果節點不為null,返回節點的key值,否則返回null
 */
static <K,V> K keyOrNull(TreeMap.Entry<K,V> e) {
    return (e == null) ? null : e.key;
}

TreeMap中value()方法:

value() 方法返回 TreeMap中值的集合:

/**
 * 通過 new Values() 來實現,返回TreeMap中值的集合
 * Values() 是集合類Value的構造函數
 */
public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new Values();
        values = vs;
    }
    return vs;
}

/**
 * 集合類Value
 */
class Values extends AbstractCollection<V> {
    // 返回迭代器
    public Iterator<V> iterator() {
        // iterator() 通過ValueIterator() 返回迭代器
        return new ValueIterator(getFirstEntry());
    }
    
    // 返回個數
    public int size() {
        return TreeMap.this.size();
    }

    // TreeMap的值的集合中 是否包含 對象o
    public boolean contains(Object o) {
        return TreeMap.this.containsValue(o);
    }

    // 刪除TreeMap的值的集合中的對象o
    public boolean remove(Object o) {
        for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e)) {
            if (valEquals(e.getValue(), o)) {
                deleteEntry(e);
                return true;
            }
        }
        return false;
    }

    // 清空TreeMap的值的集合
    public void clear() {
        TreeMap.this.clear();
    }

    public Spliterator<V> spliterator() {
        return new ValueSpliterator<K,V>(TreeMap.this, null, null, 0, -1, 0);
    }
}

/**
 * ValueIterator類實現 Iterator接口實現的next()方法
 */
final class ValueIterator extends PrivateEntryIterator<V> {
        ValueIterator(Entry<K,V> first) {
        super(first);
    }
    public V next() {
        return nextEntry().value;
    }
}
/**
 * PrivateEntryIterator類實現 Iterator接口的hasNext()和remove()方法
 * ValueIterator類實現 Iterator接口實現的next()方法
 */
abstract class PrivateEntryIterator<T> implements Iterator<T> {
    // 下一節點
    Entry<K,V> next;
    // 上一次返回的節點
    Entry<K,V> lastReturned;
    // 修改次數統計數
    int expectedModCount;

    PrivateEntryIterator(Entry<K,V> first) {
        expectedModCount = modCount;
        lastReturned = null;
        next = first;
    }

    // 是否存在下一個節點
    public final boolean hasNext() {
        return next != null;
    }

    // 返回下一個節點
    final Entry<K,V> nextEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        next = successor(e);
        lastReturned = e;
        return e;
    }

    // 返回上一節點
    final Entry<K,V> prevEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        next = predecessor(e);
        lastReturned = e;
        return e;
    }

    // 刪除當前節點
    public void remove() {
        if (lastReturned == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // deleted entries are replaced by their successors
        if (lastReturned.left != null && lastReturned.right != null)
            next = lastReturned;
        deleteEntry(lastReturned);
        expectedModCount = modCount;
        lastReturned = null;
    }
}
PrivateEntryIterator 類

TreeMap的entrySet()方法:

 entrySet() 方法返回 TreeMap的鍵值對的集合:

/**
 * 通過 new EntrySet() 來實現,返回TreeMap的鍵值對集合
 */
public Set<Map.Entry<K,V>> entrySet() {
    EntrySet es = entrySet;
    return (es != null) ? es : (entrySet = new EntrySet());
}

/**
 * EntrySet是TreeMap的所有鍵值對組成的集合,它的單位是單個鍵值對
 */
class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    // 返回迭代器
    public Iterator<Map.Entry<K,V>> iterator() {
        return new EntryIterator(getFirstEntry());
    }

    // EntrySet中是否包含 鍵值對Object
    public boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
        Object value = entry.getValue();
        Entry<K,V> p = getEntry(entry.getKey());
        return p != null && valEquals(p.getValue(), value);
    }

    // 刪除EntrySet中的 鍵值對Object
    public boolean remove(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
        Object value = entry.getValue();
        Entry<K,V> p = getEntry(entry.getKey());
        if (p != null && valEquals(p.getValue(), value)) {
            deleteEntry(p);
            return true;
        }
        return false;
    }

    // 返回EntrySet中元素個數
    public int size() {
        return TreeMap.this.size();
    }

    // 清空EntrySet
    public void clear() {
        TreeMap.this.clear();
    }

    public Spliterator<Map.Entry<K,V>> spliterator() {
        return new EntrySpliterator<K,V>(TreeMap.this, null, null, 0, -1, 0);
    }
}

/**
 * EntryIterator類實現 Iterator接口實現的next()方法
 */
final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> {
    EntryIterator(Entry<K,V> first) {
        super(first);
    }
    public Map.Entry<K,V> next() {
        return nextEntry();
    }
}

TreeMap實現的Cloneable接口:

TreeMap實現了Cloneable接口,即實現了clone()方法。
clone()方法的作用很簡單,就是克隆一個TreeMap對象並返回。

/**
 * 克隆一個TreeMap,並返回Object對象
 */
public Object clone() {
    TreeMap<K,V> clone = null;
    try {
        clone = (TreeMap<K,V>) super.clone();
    } catch (CloneNotSupportedException e) {
        throw new InternalError();
    }

    // Put clone into "virgin" state (except for comparator)
    clone.root = null;
    clone.size = 0;
    clone.modCount = 0;
    clone.entrySet = null;
    clone.navigableKeySet = null;
    clone.descendingMap = null;

    // Initialize clone with our mappings
    try {
        clone.buildFromSorted(size, entrySet().iterator(), null, null);
    } catch (java.io.IOException cannotHappen) {
    } catch (ClassNotFoundException cannotHappen) {
    }

    return clone;
}

TreeMap實現的Serializable接口:

TreeMap實現java.io.Serializable,分別實現了串行讀取和寫入功能:

  • 串行寫入函數是writeObject(),它的作用是將TreeMap的“容量和所有的Entry”都寫入到輸出流中。
  • 串行讀取函數是readObject(),它的作用是將TreeMap的“容量和所有的Entry”依次讀出。

readObject() 和 writeObject() 正好是一對,通過它們,我能實現TreeMap的串行傳輸。

/**
 * java.io.Serializable的寫入函數
 * 將TreeMap的 容量和所有的Entry 都寫入到輸出流中
 */
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // Write out the Comparator and any hidden stuff
    s.defaultWriteObject();

    // Write out size (number of Mappings)
    s.writeInt(size);

    // Write out keys and values (alternating)
    for (Iterator<Map.Entry<K,V>> i = entrySet().iterator(); i.hasNext(); ) {
        Map.Entry<K,V> e = i.next();
        s.writeObject(e.getKey());
        s.writeObject(e.getValue());
    }
}

/**
 * java.io.Serializable的讀取函數:根據寫入方式讀出
 * 將TreeMap的 容量和所有的Entry 依次讀出
 */
private void readObject(final java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Read in the Comparator and any hidden stuff
    s.defaultReadObject();

    // Read in size
    int size = s.readInt();

    buildFromSorted(size, null, s, null);
}

三、TreeMap使用例子

1.TreeMap常用方法使用Demo:

import java.util.*;

/**
 * @author nana
 * @date 2019/2/23
 */
public class TreeMapDemo {

    public static void main(String[] args) {
        // 測試常用的API
        testTreeMapOrdinaryAPIs();

        // 測試TreeMap導航函數
        testNavigableMapAPIs();

        // 測試TreeMap的子Map函數
        testSubMapAPIs();
    }

    /**
     * 測試常用的API
     */
    private static void testTreeMapOrdinaryAPIs() {
        // 生成隨機數
        Random random = new Random();

        // 創建TreeMap實例
        TreeMap treeMap = new TreeMap();
        treeMap.put("one", random.nextInt(10));
        treeMap.put("two", random.nextInt(10));
        treeMap.put("three", random.nextInt(10));

        System.out.println("TreeMapDemo.testTreeMapOrdinaryAPIs-Begin");

        // 打印TreeMap
        System.out.printf("打印treeMap:\n%s\n", treeMap);;

        // 通過Iterator遍歷key-value
        Iterator iterator = treeMap.entrySet().iterator();
        System.out.println("通過Iterator遍歷key-value:");
        while (iterator.hasNext()) {
            Map.Entry entity = (Map.Entry) iterator.next();
            System.out.printf("%s-%s\n", entity.getKey(), entity.getValue());
        }

        // TreeMap的鍵值對個數
        System.out.printf("TreeMap的鍵值對個數:%s\n", treeMap.size());

        // 是否包含key
        System.out.println("是否包含key:");
        System.out.printf("是否包含key:one-%s\n", treeMap.containsKey("one"));
        System.out.printf("是否包含key:four-%s\n", treeMap.containsKey("four"));

        // 刪除key對應的鍵值對
        System.out.println("刪除key對應的鍵值對:");
        treeMap.remove("one");
        System.out.printf("刪除key為one的鍵值對后,treeMap為:\n%s\n", treeMap);

        // 清空TreeMap的節點
        System.out.println("清空treeMap的節點:");
        treeMap.clear();
        System.out.printf("%s\n", treeMap.isEmpty() ? "treeMap is empty!" : "treeMap is not empty!");
        System.out.printf("%s\n", treeMap == null ? "treeMap is null!" : "treeMap is not null!");

        System.out.println("TreeMapDemo.testTreeMapOrdinaryAPIs-End");
    }

    /**
     * 測試TreeMap導航函數
     */
    private static void testNavigableMapAPIs() {
        // 創建TreeMap實例,TreeMap是 NavigableMap接口的實現類
        NavigableMap navigableMap = new TreeMap();
        navigableMap.put("aaa",1);
        navigableMap.put("bbb",2);
        navigableMap.put("ccc",3);
        navigableMap.put("ddd",4);

        System.out.println("TreeMapDemo.testNavigableMapAPIs-Begin");

        // 打印TreeMap
        System.out.printf("打印navigableMap:\n%s\n", navigableMap);

        // 獲取第一個key和節點
        System.out.printf("First key:%s\tFirst entry:%s\n", navigableMap.firstKey(), navigableMap.firstEntry());

        // 獲取最后一個key和節點
        System.out.printf("Last key:%s\tLast entry:%s\n", navigableMap.lastKey(), navigableMap.lastEntry());

        // 獲取小於/等於 key為bbb 最大的key和節點
        System.out.printf("Key floor before bbb:%s\t%s\n", navigableMap.floorKey("bbb"), navigableMap.floorEntry("bbb"));

        // 獲取小於 key為bbb 最大的key和節點
        System.out.printf("Key lower before bbb:%s\t%s\n", navigableMap.lowerKey("bbb"), navigableMap.lowerEntry("bbb"));

        // 獲取大於/等於 key為bbb 最大的key和節點
        System.out.printf("Key ceiling after bbb:%s\t%s\n", navigableMap.ceilingKey("bbb"), navigableMap.ceilingEntry("bbb"));

        // 獲取大於 key為bbb 最大的key和節點
        System.out.printf("Key higher after bbb:%s\t%s\n", navigableMap.higherKey("bbb"), navigableMap.higherEntry("bbb"));

        System.out.println("TreeMapDemo.testNavigableMapAPIs-End");
    }

    /**
     * 測試TreeMap的子Map函數
     */
    private static void testSubMapAPIs() {
        // 實例化TreeMap對象
        TreeMap treeMap = new TreeMap();
        treeMap.put("a",1);
        treeMap.put("b",2);
        treeMap.put("c",3);
        treeMap.put("d",4);

        System.out.println("TreeMapDemo.testSubMapAPIs-Begin");

        // 打印TreeMap
        System.out.printf("打印TreeMap:\n%s\n", treeMap);

        // 打印 key為c節點 前的節點(默認不包含c節點)
        System.out.printf("打印 key為c節點 前的節點(默認不包含c節點):%s", treeMap.headMap("c"));

        System.out.printf("打印 key為c節點 前的節點(包含c節點):%s\n", treeMap.headMap("c", true));
        System.out.printf("打印 key為c節點 前的節點(不包含c節點):%s\n", treeMap.headMap("c", false));

        // 打印 key為c節點 后的節點(默認包含c節點)
        System.out.printf("打印 key為c節點 后的節點(默認包含c節點):%s\n", treeMap.tailMap("c"));

        System.out.printf("打印 key為c節點 后的節點(包含c節點)%s\n", treeMap.tailMap("c", true));
        System.out.printf("打印 key為c節點 后的節點(不包含c節點)%s\n", treeMap.tailMap("c", false));

        // 打印 key為a與c節點 之間的節點(默認不包含c節點)
        System.out.printf("打印 key為a與c節點 之間的節點(默認包含c節點):\n%s\n", treeMap.subMap("a", "c"));

        System.out.printf("打印 key為a與c節點 之間的節點(包含a、c節點):\n%s\n", treeMap.subMap("a", true, "c", true));
        System.out.printf("打印 key為a與c節點 之間的節點(包含a節點):\n%s\n", treeMap.subMap("a", true, "c", false));
        System.out.printf("打印 key為a與c節點 之間的節點(包含c節點):\n%s\n", treeMap.subMap("a", false, "c", true));
        System.out.printf("打印 key為a與c節點 之間的節點(不包含a、c節點):\n%s\n", treeMap.subMap("a", false, "c", false));

        // 正序打印TreeMap的key
        System.out.printf("正序打印TreeMap的key:\n%s\n", treeMap.navigableKeySet());
        // 倒序打印TreeMap的key
        System.out.printf("倒序打印TreeMap的key:\n%s\n", treeMap.descendingKeySet());

        System.out.println("TreeMapDemo.testSubMapAPIs-End");
    }
}
TreeMapDemo
TreeMapDemo.testTreeMapOrdinaryAPIs-Begin
打印treeMap:
{one=2, three=5, two=1}
通過Iterator遍歷key-value:
one-2
three-5
two-1
TreeMap的鍵值對個數:3
是否包含key:
是否包含key:one-true
是否包含key:four-false
刪除key對應的鍵值對:
刪除key為one的鍵值對后,treeMap為:
{three=5, two=1}
清空treeMap的節點:
treeMap is empty!
treeMap is not null!
TreeMapDemo.testTreeMapOrdinaryAPIs-End
TreeMapDemo.testNavigableMapAPIs-Begin
打印navigableMap:
{aaa=1, bbb=2, ccc=3, ddd=4}
First key:aaa    First entry:aaa=1
Last key:ddd    Last entry:ddd=4
Key floor before bbb:bbb    bbb=2
Key lower before bbb:aaa    aaa=1
Key ceiling after bbb:bbb    bbb=2
Key higher after bbb:ccc    ccc=3
TreeMapDemo.testNavigableMapAPIs-End
TreeMapDemo.testSubMapAPIs-Begin
打印TreeMap:
{a=1, b=2, c=3, d=4}
打印 key為c節點 前的節點(默認不包含c節點):{a=1, b=2}打印 key為c節點 前的節點(包含c節點):{a=1, b=2, c=3}
打印 key為c節點 前的節點(不包含c節點):{a=1, b=2}
打印 key為c節點 后的節點(默認包含c節點):{c=3, d=4}
打印 key為c節點 后的節點(包含c節點){c=3, d=4}
打印 key為c節點 后的節點(不包含c節點){d=4}
打印 key為a與c節點 之間的節點(默認包含c節點):
{a=1, b=2}
打印 key為a與c節點 之間的節點(包含a、c節點):
{a=1, b=2, c=3}
打印 key為a與c節點 之間的節點(包含a節點):
{a=1, b=2}
打印 key為a與c節點 之間的節點(包含c節點):
{b=2, c=3}
打印 key為a與c節點 之間的節點(不包含a、c節點):
{b=2}
正序打印TreeMap的key:
[a, b, c, d]
倒序打印TreeMap的key:
[d, c, b, a]
TreeMapDemo.testSubMapAPIs-End
TreeMapDemo 運行結果

2.TreeMap遍歷使用Demo:

import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @author nana
 * @date 2019/2/23
 */
public class TreeMapIteratorTest {

    public static void main(String[] args) {
        // 創建treeMap對象
        TreeMap treeMap = treeMapTest();

        // 通過entrySet()遍歷TreeMap的節點
        iteratorTreeMapByEntrySet(treeMap);
        // 通過keySet()遍歷TreeMap的節點
        iteratorTreeMapByKeySet(treeMap);
        // 遍歷TreeMap的value
        iteratorTreeMapByValue(treeMap);
    }

    /**
     * 創建treeMap對象
     * @return
     */
    private static TreeMap treeMapTest() {
        String key = null;
        int keyValue = 0;
        Integer value = null;
        Random random = new Random();
        TreeMap treeMap = new TreeMap();
        int i = 0;
        while (i < 6) {
            // 隨機獲取[0,50)的整數
            keyValue = random.nextInt(50);
            key = String.valueOf(keyValue);
            value = random.nextInt(10);
            // 添加到treeMap中
            treeMap.put(key, value);
            i++;
        }
        return treeMap;
    }

    /**
     * 通過entrySet()遍歷TreeMap的節點
     */
    private static void iteratorTreeMapByEntrySet(TreeMap treeMapTest) {
        if (StringUtils.isEmpty(treeMapTest)) {
            return;
        }
        // 遍歷TreeMap
        Iterator iterator = treeMapTest.entrySet().iterator();
        System.out.println("通過entrySet()遍歷TreeMap的節點:");
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            System.out.printf("%s-%s\t", entry.getKey(), entry.getValue());
        }
    }

    /**
     * 通過keySet()遍歷TreeMap的節點
     */
    private static void iteratorTreeMapByKeySet(TreeMap treeMapTest) {
        if(StringUtils.isEmpty(treeMapTest)) {
            return;
        }
        String key = null;
        Integer value = null;
        Iterator iterator = treeMapTest.keySet().iterator();
        System.out.println("\n通過keySet()遍歷TreeMap的節點:");
        while (iterator.hasNext()) {
            key = (String) iterator.next();
            value = (Integer) treeMapTest.get(key);
            System.out.printf("%s-%s\t", key, value);
        }
    }

    /**
     * 遍歷TreeMap的value
     */
    private static void iteratorTreeMapByValue(TreeMap treeMapTest) {
        if (treeMapTest == null) {
            return;
        }
        Collection collection = treeMapTest.values();
        Iterator iterator = collection.iterator();
        System.out.println("\n遍歷TreeMap的value:");
        while (iterator.hasNext()) {
            System.out.printf("%s\t", iterator.next());
        }
    }
    
}
TreeMapIteratorTest
通過entrySet()遍歷TreeMap的節點:
19-0    2-7    20-1    22-0    34-3    
通過keySet()遍歷TreeMap的節點:
19-0    2-7    20-1    22-0    34-3    
遍歷TreeMap的value:
0    7    1    0    3    
TreeMapIteratorTest 運行結果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM