Java集合体系のAbstractMapソースコード分析


  • 前言
  • ソースコード分析
  • 構造方法
  • メンバー変数
  • 抽象メソッド
  • インスタンスメソッド
  • クエリー操作
  • 修正操作
  • 一括操作
  • ビュー
  • 比較とハッシュ
  • その他の方法
  • SimpleEntry
  • SimpleImmutableEntry

  • まとめ

  • 前言
    AbstraactMapは、このインタフェースの実装に伴うワークロードを最小限に抑えるために、Mapインタフェースを実装する抽象クラスです.
    修正不可能なMapを実装するには、このような拡張を行い、EntrySet()メソッド(AbstractMap固有の抽象メソッド)を実装するだけでよい.このメソッドはMapマッピング関係が格納されているSetの集合を返す.通常、返されたSetはAbstractSet上で実装される.このSetはadd()またはremove()メソッドをサポートするべきではない.その反復器もremove()メソッドをサポートするべきではありません.
    修正可能なMapを実現するには、このようなputメソッドを別途書き直さなければならない(そうでなければ、U n s p p p p p p r o t e d OperationException異常が投げ出される).iterator()が返す反復器も、そのremove()メソッドを別途実装する必要があります.
    Mapインタフェース仕様の推奨に従って、void(パラメータなし)構築方法とmap構築方法を提供する必要があります.
    ソース分析
    構築方法
    サブクラスコンストラクタの呼び出しに使用される唯一の非パラメトリックコンストラクタです.通常は暗黙的です.
    protected AbstractMap() {
    }

    メンバー変数
    メンバー変数は次の2つです.
    transient Set<K>        keySet;
    transient Collection<V> values;

    いずれもtransientキーワードで修飾され、シーケンス化時に保存されないことを示します.しかも宣言しただけで、付与されていません.割り当ては、次のkeySet()メソッドとvalues()メソッドで実行されます.-keySet:すべてのkeyのSetセットを保存します.-values:valueのCollectionコレクションをすべて保存します.
    抽象メソッド
    抽象メソッドは1つだけで、Mapのすべてのマッピングエントリを保存するSetセットを返します.
    public abstract Set> entrySet();

    インスタンスメソッド
    クエリー操作
    コレクションの長さを返します.
    public int size() {
        return entrySet().size();
    }

    戻りセットが空であるかどうか、空である場合trueを返します.そうでない場合falseを返します.
    public boolean isEmpty() {
        //            0     true
        return size() == 0;
    }

    指定したvalue(nullサポート)がコレクションに存在するかどうかを返し、存在する場合trueを返します.そうでない場合falseを返します.
    public boolean containsValue(Object value) {
        //      
        Iterator> i = entrySet().iterator();
        //        value       value     ,       true
        if (value==null) {
            while (i.hasNext()) {
                Entry e = i.next();
                if (e.getValue()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry e = i.next();
                if (value.equals(e.getValue()))
                    return true;
            }
        }
        //     ,   false
        return false;
    }

    指定したkey(nullをサポート)がコレクションに存在するかどうかを返し、存在する場合はtrueを返します.そうでない場合はfalseを返します.
    public boolean containsKey(Object key) {
        //      
        Iterator> i = entrySet().iterator();
        //        key       key     ,       true
        if (key==null) {
            while (i.hasNext()) {
                Entry e = i.next();
                if (e.getKey()==null)
                    return true;
            }
        } else {
            while (i.hasNext()) {
                Entry e = i.next();
                if (key.equals(e.getKey()))
                    return true;
            }
        }
        //     ,   false
        return false;
    }

    指定したkeyがマッピングしたvalueを取得し、見つからない場合nullを返します.
    public V get(Object key) {
        //      
        Iterator> i = entrySet().iterator();
        //     ,       key           value
        if (key==null) {
            while (i.hasNext()) {
                Entry e = i.next();
                if (e.getKey()==null)
                    return e.getValue();
            }
        } else {
            while (i.hasNext()) {
                Entry e = i.next();
                if (key.equals(e.getKey()))
                    return e.getValue();
            }
        }
        //     ,   null
        return null;
    }

    操作の変更
    指定したkeyをvalueに関連付けます(オプションで、インプリメンテーションクラスがメソッドを実装できるか、実装しないかを意味します.最初に説明したように、変更可能なセットを実装したい場合は、変更可能なアクションに関するメソッドを書き換えることができます):
    public V put(K key, V value) {
        // AbstractMap             
        throw new UnsupportedOperationException();
    }

    指定したkeyのマッピングエントリを削除し、削除に成功すると指定したkeyがマッピングしたvalueを返し、削除に失敗しnullを返します.
    public V remove(Object key) {
        //      
        Iterator> i = entrySet().iterator();
        //           ,     null
        Entry correctEntry = null;
        //   correntEntry                 
        //                key         
        if (key==null) {
            while (correctEntry==null && i.hasNext()) {
                Entry e = i.next();
                if (e.getKey()==null)
                    correctEntry = e;
            }
        } else {
            while (correctEntry==null && i.hasNext()) {
                Entry e = i.next();
                if (key.equals(e.getKey()))
                    correctEntry = e;
            }
        }
    
        V oldValue = null;
        //        key      ,     null  
        if (correctEntry !=null) {
            //    key      value,         
            oldValue = correctEntry.getValue();
            //           ,       
            //        remove()           
            //                         remove()   
            i.remove();
        }
        //      key               value,     null
        return oldValue;
    }

    一括アクション
    指定したパラメータMapセットのマッピングデータを現在のMapに格納します.
    public void putAll(Map extends K, ? extends V> m) {
        //       Map        
        for (Map.Entry extends K, ? extends V> e : m.entrySet())
            //       key、value          Map 
            //         put()   ,           put()   ,           
            put(e.getKey(), e.getValue());
    }

    現在のMapを空にします.
    public void clear() {
        entrySet().clear();
    }

    表示
    すべてのkeyを格納するSetビューのセットを返します.
    public Set keySet() {
        Set ks = keySet;
        //   key        ,    AbstractSet       
        if (ks == null) {
            ks = new AbstractSet() {
                // AbstractSet       ,           
                public Iterator iterator() {
                    //               enterySet()       
                    return new Iterator() {
                        private Iterator> i = entrySet().iterator();
    
                        public boolean hasNext() {
                            return i.hasNext();
                        }
    
                        public K next() {
                            return i.next().getKey();
                        }
    
                        public void remove() {
                            i.remove();
                        }
                    };
                }
                //                 AbstractMap      
                public int size() {
                    return AbstractMap.this.size();
                }
    
                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }
    
                public void clear() {
                    AbstractMap.this.clear();
                }
    
                public boolean contains(Object k) {
                    return AbstractMap.this.containsKey(k);
                }
            };
            keySet = ks;
        }
        //    key   Set     
        return ks;
    }

    すべてのvalueを格納するCollectionビューのセットを返します.
    public Collection values() {
        //         keySet()   
        //      Set   AbstractSet     ,    Collection   AbstractCollection     。
        Collection vals = values;
        if (vals == null) {
            vals = new AbstractCollection() {
                public Iterator iterator() {
                    return new Iterator() {
                        private Iterator> i = entrySet().iterator();
    
                        public boolean hasNext() {
                            return i.hasNext();
                        }
    
                        public V next() {
                            return i.next().getValue();
                        }
    
                        public void remove() {
                            i.remove();
                        }
                    };
                }
    
                public int size() {
                    return AbstractMap.this.size();
                }
    
                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }
    
                public void clear() {
                    AbstractMap.this.clear();
                }
    
                public boolean contains(Object v) {
                    return AbstractMap.this.containsValue(v);
                }
            };
            values = vals;
        }
        return vals;
    }

    比較とハッシュ
    指定したオブジェクトが現在のMapに等しいかどうかを比較します.
    public boolean equals(Object o) {
        //        ,     true
        if (o == this)
            return true;
        //           Map         ,   false
        if (!(o instanceof Map))
            return false;
        //     map
        Map,?> m = (Map,?>) o;
        //       Map   ,   false
        if (m.size() != size())
            return false;
    
        try {
            //      Map    
            Iterator> i = entrySet().iterator();
            //       
            while (i.hasNext()) {
                //    value     map     value     ,       false 
                Entry e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(m.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused) {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
        //              ,     true
        return true;
    }

    現在のMapのhashCodeを返します.
    public int hashCode() {
        int h = 0;
        Iterator> i = entrySet().iterator();
        while (i.hasNext())
            // AbstractMap   hashCode   
            //           hashCode    
            h += i.next().hashCode();
        return h;
    }

    その他の方法
    現在のMapの文字列表現を返します.
    public String toString() {
        //      
        Iterator> i = entrySet().iterator();
        //      ,    :{}
        if (! i.hasNext())
            return "{}";
        //    StringBuilder   ,       
        StringBuilder sb = new StringBuilder();
        //      ,      : {
        sb.append('{');
        for (;;) {
            //         key、value
            Entry e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            //   key    value     Map  ,   (this Map),      key、value
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            //      ,      :},   
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

    このAbstractMapインスタンスの浅いコピーを返します(キーと値自体はクローンされません):
    protected Object clone() throws CloneNotSupportedException {
        //    Native   
        AbstractMap,?> result = (AbstractMap,?>)super.clone();
        //     key、value       null
        result.keySet = null;
        result.values = null;
        return result;
    }

    SimpleEntryおよびSimpleImmutableEntry内部クラス用のプライベート静的メソッド.指定した2つのオブジェクトが等しいかどうかを確認します(nullを含む):
    private static boolean eq(Object o1, Object o2) {
        return o1 == null ? o2 == null : o1.equals(o2);
    }

    SimpleEntry
    Map.Entryインタフェースの実装クラスは、1.6から提供されます.keyとvalueマッピングのエントリを保存します.この内部クラスのvalueはsetValue()メソッドで変更できます.
    public static class SimpleEntry<K,V>
        implements Entry, java.io.Serializable
    {
        private static final long serialVersionUID = -8499721149061103585L;
    
        private final K key;
        private V value;
    
        /**
         *      key、value      。
         */
        public SimpleEntry(K key, V value) {
            this.key   = key;
            this.value = value;
        }
    
        /**
         *             key、value        。
         */
        public SimpleEntry(Entry extends K, ? extends V> entry) {
            this.key   = entry.getKey();
            this.value = entry.getValue();
        }
    
        /**
         *    key
         */
        public K getKey() {
            return key;
        }
    
        /**
         *    value
         */
        public V getValue() {
            return value;
        }
    
        /**
         *      value  ,      value  。
         */
        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
    
        /**
         *                  ,       true,     false。
         */
        public boolean equals(Object o) {
            //          Map.Entry             false
            if (!(o instanceof Map.Entry))
                return false;
            //     Map.Entry
            Map.Entry,?> e = (Map.Entry,?>)o;
            //    eq()       key   value     
            return eq(key, e.getKey()) && eq(value, e.getValue());
        }
    
        /**
         *         hasCode。
         */
        public int hashCode() {
            // hasCode        key   value   hasCode   
            return (key   == null ? 0 :   key.hashCode()) ^
                   (value == null ? 0 : value.hashCode());
        }
    
        /**
         *               。
         */
        public String toString() {
            return key + "=" + value;
        }
    
    }

    SimpleImmutableEntry
    その名の通り、不変のSimpleEntry.この内部クラスは同様にMapである.Entryインタフェースの実装クラスは、1.6から提供されます.SimpleEntryと唯一の違いは、setValue()メソッドが例外を投げ出すことです.
    public static class SimpleImmutableEntry
        implements Entry, 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) {
            // look here
            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;
        }
    }

    これでAbstractMap抽象クラスのソースコードが読み終わります.最後にまとめてみます.
    まとめ
    AbstraactMapはMapインタフェースを実現する抽象クラスとして,その役割と目的はMapインタフェースの実現に伴う作業量を簡略化することである.よく使われるTreeMap、HashMapなどは、それから受け継がれています.私たちが自分でフレームワークを設計するときも、実はこのような設計思想を採用することができます.