Java集合体系のAbstractMapソースコード分析
前言
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などは、それから受け継がれています.私たちが自分でフレームワークを設計するときも、実はこのような設計思想を採用することができます.