Javaコレクション(六)--TreeMapの概要

9351 ワード

本編ではTreeMapを分析する.
TreeMapの定義と説明
次のように定義します.
public class TreeMap
    extends AbstractMap
    implements NavigableMap, Cloneable, java.io.Serializable{}p

1、AbstractMapを継承し、NavigableMapインタフェースを実現した.NavigableMapはSortedMapインタフェースを拡張したインタフェースであることを集合フレームワークの記事から知った.一方、SortedMapインタフェースは、主に秩序化されたMap実装を提供します.だからTreeMapは秩序あるMapです.
2、Cloneableを実現し、cloneをサポートする.
3、javaを実現した.io.Serializableは、シーケンス化と逆シーケンス化をサポートします.
TreeMapは赤と黒の木に基づいて実現された.使用するコンストラクション関数に応じて、キーの比較可能な自然な順序、または作成時に提供されるComparatorに基づいてソートします.
次に、構造方法を見てみましょう.
//   ,    TreeMap      ,            ,  null。
private final Comparator super K> comparator;

//               TreeMap
public TreeMap() {
        //    null
        comparator = null;
    }
    
//    ,      TreeMap
public TreeMap(Comparator super K> comparator) {
        //        
        this.comparator = comparator;
    }

//            TreeMap,             
public TreeMap(Map extends K, ? extends V> m) {
        comparator = null;
        //               TreeMap。       TreeMap                    
        putAll(m);
    }

//     m            TreeMap
public TreeMap(SortedMap m) {
        //     m           
        comparator = m.comparator();
        try {
           
             //              
             buildFromSorted(m.size(),m.entrySet().iterator(), null, null);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }
    }

コンストラクション関数から,コンパレータを指定しない場合,キーの自然な順序ソートを用いることが分かった.
TreeMapソースコードの概要
慣例に従って、TreeMapに付与されたput()メソッドを見に行きます.
//   
private final Comparator super K> comparator;

//       
private transient TreeMapEntry root;

//TreeMap   
private transient int size = 0;

//TreeMap    
private transient int modCount = 0;

public V put(K key, V value) {
        //        t
        TreeMapEntry t = root;
        //        null
        if (t == null) {
            //    null,        null
            if (comparator != null) {
                //    null,  key   null
                if (key == null) {
                    //    null     
                    comparator.compare(key, key);
                }
            } else {
                //       null,  key   null
                if (key == null) {
                    //key null,    
                    throw new NullPointerException("key == null");
                } else if (!(key instanceof Comparable)) {
                //comparator null,key  null,  key      Comparable  ,     
                    throw new ClassCastException(
                            "Cannot cast" + key.getClass().getName() + " to Comparable.");
                }
            }
            //         
            root = new TreeMapEntry<>(key, value, null);
            
            size = 1;
            modCount++;
            return null;
        }
        
        //       ,      
        //key      key     
        int cmp;
        // key        
        TreeMapEntry parent;
        //          cpr 
        Comparator super K> cpr = comparator;
        //        null
        if (cpr != null) {
            //     null,    
            do {
                // t    parent(      root)
                parent = t;
                //           key      key
                cmp = cpr.compare(key, t.key);
                //  key   t             
                if (cmp < 0)
                    t = t.left;
                //  key   t             
                else if (cmp > 0)
                    t = t.right;
                else
                    //  key   ,         
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            //      null,  key   null
            if (key == null)
                //key null,     
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
            //    ,key     Comparable   ,     ClassCastException。
                Comparable super K> k = (Comparable super K>) key;
            //        
            do {
                // t    parent(      root)
                parent = t;
                // key      key   
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                //  key   t             
                    t = t.left;
                else if (cmp > 0)
                //  key   t             
                    t = t.right;
                else
                //  key   ,         
                    return t.setValue(value);
            } while (t != null);
        }
        //        
        TreeMapEntry e = new TreeMapEntry<>(key, value, parent);
        //  key      key  
        if (cmp < 0)
            //              
            parent.left = e;
        else
            //  ,              
            parent.right = e;
        //    ,                    
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

private static final boolean RED   = false;
private static final boolean BLACK = true;

//     
static final class TreeMapEntry implements Map.Entry {
       
        //key 
        K key;
        
        //value 
        V value;
        
        //   
        TreeMapEntry left;
        
        //   
        TreeMapEntry right;
        
        //   
        TreeMapEntry parent;
        
        //    
        boolean color = BLACK;

        //   
        TreeMapEntry(K key, V value, TreeMapEntry parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

        //    
        ..........
    }

以上の解析から,TreeMapは実際にTreeMapEntryタイプのroot属性を操作することによって,データの挿入を実現していることが分かった.つまり、TreeMapに対する操作は、実は赤と黒の木に対する操作によって行われています.
次に、他の方法を分析します.
TreeMapEntryの操作
TreeMapの操作方法としては、firstEntry()、lastEntry()、pollFirstEntry()、pollLastEntry()、lowerEntry()、floorEntry()、ceilingEntry()、higherEntry()がある.これらは、指定したノードをツリーのループで見つけて操作します.例えばfirstEntry():
//         
public Map.Entry firstEntry() {
        return exportEntry(getFirstEntry());
    }

//              
final TreeMapEntry getFirstEntry() {
        //       ,         
        TreeMapEntry p = root;
        if (p != null)
            while (p.left != null)
                p = p.left;
        return p;
    }

//    AbstractMap.SimpleImmutableEntry
static  Map.Entry exportEntry(TreeMapEntry e) {
        return (e == null) ? null :
            new AbstractMap.SimpleImmutableEntry<>(e);
    }

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

上の分析では、見知らぬクラス、AbstractMapを発見しました.SimpleImmutableEntry.このクラスは、setValue()メソッドをサポートしないキーと値を可変にするためです.これは私たちが戻ってきたEntryを修正しないようにするためです.
key操作に関する方法
キーの操作方法はfirstKey()、lastKey()、lowerKey()、floorKey()、ceilingKey()、higherKey()があります.firstKey()の方法を見てみましょう.
public K firstKey() {
        return key(getFirstEntry());
    }
    
 static  K key(TreeMapEntry e) {
        if (e==null)
            throw new NoSuchElementException();
        return e.key;
    }   


すなわち,ノードのTreeMapEntryオブジェクトを取得することにより,対応するkeyを取得する.
value操作に関する方法
valueの操作方法はcontainsValue()、get()、values()などがありますが、get()の方法を見てみましょう.
 public V get(Object key) {
        //       key   
        TreeMapEntry p = getEntry(key); 
         //   
        return (p==null ? null : p.value);
    }
    
final TreeMapEntry getEntry(Object key) {
        //         
        if (comparator != null)
            return getEntryUsingComparator(key);
        //  key   null    
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
        //      key    
            Comparable super K> k = (Comparable super K>) key;
        // root    t    
        TreeMapEntry p = root;
        //          key   ,    null
        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;
    }    

実際には、指定したノードをツリーのループで取得し、指定した値を取得していることがわかります.
ツリーに関連するアクション
操作木に関する方法としては,左回り(rotateLeft()),右回り(rotateRight()),挿入補正(fixAfterInsertion()),削除補正(fixAfterDeletion()などがある.左回りを見てみましょう.
private void rotateLeft(TreeMapEntry p) {
        if (p != null) {
            TreeMapEntry r = p.right;
            p.right = r.left;
            if (r.left != null)
                r.left.parent = p;
            r.parent = p.parent;
            if (p.parent == null)
                root = r;
            else if (p.parent.left == p)
                p.parent.left = r;
            else
                p.parent.right = r;
            r.left = p;
            p.parent = r;
        }
    }

では、この文章はここまで分析して、後期に補充があるかどうかを見てみましょう.