Java集合之TreeMap源碼分析


一、概述

  TreeMap是基於紅黑樹實現的。由於TreeMap實現了java.util.sortMap接口,集合中的映射關系是具有一定順序的,該映射根據其鍵的自然順序進行排序或者根據創建映射時提供的Comparator進行排序,具體取決於使用的構造方法。另外TreeMap中不允許鍵對象是null。

  1、什么是紅黑樹?

  紅黑樹是一種特殊的二叉排序樹,主要有以下幾條基本性質:

  • 每個節點都只能是紅色或者黑色
  • 根節點是黑色
  • 每個葉子節點是黑色的
  • 如果一個節點是紅色的,則它的兩個子節點都是黑色的
  • 從任意一個節點到每個葉子節點的所有路徑都包含相同數目的黑色節點

  紅黑樹的具體原理分析和算法設計可參見博文:紅黑樹的原理分析和算法設計

  2、key的兩種排序方式

  自然排序:TreeMap的所有key必須實現Comparable接口,並且所有key應該是同一個類的對象,否則將會拋ClassCastException異常

  指定排序:這種排序需要在構造TreeMap時,傳入一個Comparator對象,該對象負責對TreeMap中的key進行排序

  3、TreeMap類的繼承關系

public class TreeMap<K,V> 

  extends AbstractMap<K,V>

    implements NavigableMap<K,V>, Cloneable, Serializable

  其中,NavigableMap接口是擴展的SortMap,具有了針對給定搜索目標返回最接近匹配項的導航方法。其方法 lowerEntryfloorEntryceilingEntryhigherEntry 分別返回與小於、小於等於、大於等於、大於給定鍵的鍵關聯的 Map.Entry 對象,如果不存在這樣的鍵,則返回 null。類似地,方法 lowerKeyfloorKeyceilingKeyhigherKey 只返回關聯的鍵。所有這些方法是為查找條目而不是遍歷條目而設計的。 

二、TreeMap源碼分析

  1、存儲結構

  TreeMap是基於紅黑樹實現的,樹的節點定義如下:

 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;
        //構造函數
        Entry(K key, V value, Entry<K,V> parent)
        {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }
        ......
}

  2、構造函數

  TreeMap有四種構造函數,分別對應不同的參數。

   //1.使用鍵的自然順序構造一個新的、空的樹映射
    public TreeMap() 
    {
        comparator = null;
    }
    //2.構造一個新的、空的樹映射,該映射根據給定比較器進行排序
    public TreeMap(Comparator<? super K> comparator)
    {
        this.comparator = comparator;
    }
    /3.構造一個與給定映射具有相同映射關系的新的樹映射,該映射根據其鍵的自然順序 進行排序
    public TreeMap(Map<? extends K, ? extends V> m) 
    {
        comparator = null;
        putAll(m);
    }
    //4.構造一個與指定有序映射具有相同映射關系和相同排序順序的新的樹映射
    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)
        {
        }
    }

  3、TreeMap常用方法

  V put(K key,V value):將鍵值對(key,value)添加到TreeMap中

public V put(K key, V value) 
    {
        Entry<K,V> t = root;
        //若根節點為空,則以(key,value)為參數新建節點
        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) 
        {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)  //表示新增節點的key小於當前及節點的key,則以當前節點的左子節點作為新的當前節點
                    t = t.left;
                else if (cmp > 0) //表示新增節點的key大於當前及節點的key,則以當前節點的右子節點作為新的當前節點
                    t = t.right;
                else
                    return t.setValue(value);  //相等則覆蓋舊值
            } while (t != null);
        }
        //如果cpr為空,則采用默認的排序算法進行創建TreeMap集合
        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);
        }
        //將新增節點當做parent的子節點
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
      //插入新的節點后,調用fixAfterInsertion調整紅黑樹
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

  Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射關系的Set視圖

    public Set<Map.Entry<K,V>> entrySet()
     {
        EntrySet es = entrySet;
        return (es != null) ? es : (entrySet = new EntrySet());
    }

  boolean remove(Object o): 如果此 TreeMap 中存在該鍵的映射關系,則將其刪除

    public boolean remove(Object o) 
    {
                if (!(o instanceof Map.Entry))
                    return false;
                Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
                K key = entry.getKey();
                if (!inRange(key))
                    return false;
                TreeMap.Entry<K,V> node = m.getEntry(key);
                if (node!=null && valEquals(node.getValue(),
                                            entry.getValue())) 
               {
                    m.deleteEntry(node);
                    return true;
                }
                return false;
            }   
    }        

三、TreeMap應用示例代碼

public class TreeMapDemo
{
    public static void main(String[] args) 
    {
        //使用鍵的自然順序構造一個新的、空的樹映射
        TreeMap<String,String> tm=new TreeMap<>();
        tm.put("001", "中國");
        tm.put("003", "美國");
        tm.put("002", "法國");
        System.out.println("調用entrySet得到鍵值對集:");
        Set<Entry<String, String>> result=tm.entrySet();
        for(Entry<String, String> result2:result)
        {
            System.out.println(result2.getKey()+"---"+result2.getValue());
        }
        System.out.println("調用keySet得到鍵集:");
        Set<String> result3=tm.keySet();
        for(String str:result3)
        {
            System.out.println(str);
        }
        System.out.println("調用values得到值集:");
        Collection result4=tm.values();
        for(Object str:result4)
            System.out.println(str);    
        
        //新建一個帶比較器的TreeMap
        TreeMap<String,String> tm2=new TreeMap<>(new ComparatorDemo());
        tm2.put("001", "中國");
        tm2.put("003", "美國");
        tm2.put("002", "法國");
        Set<Entry<String, String>> result5=tm2.entrySet();
        for(Entry<String, String> result2:result5)
        {
            System.out.println(result2.getKey()+"---"+result2.getValue());
        }
    }
}

  首先按照鍵的自然順序構建TreeMap,加入元素並遍歷:

  

  然后新建一個比較器類,實現Comparator接口

public class ComparatorDemo implements Comparator<String>
{

    public int compare(String o1, String o2) {
        return 1;
    }

}

  在帶比較器的tm2中,按照與tm1相同的順序添加元素,此時再遍歷tm2,結果如下:

  

  這里的順序是按照比較器的compare方法得來的。由於compare方法總是返回1,即大小均相同,所以tm2中key順序為初始添加順序。    

 


免責聲明!

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



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