前言
AbstractMap 是一個實現了 Map 接口的抽象類,該類提供了 Map 接口的骨架實現,以最大限度的減少實現此接口所帶來的工作量。
要實現一個不可修改的 Map,我們只需要擴展此類,並實現 entrySet() 方法(AbstractMap 唯一的抽象方法)即可,該方法返回存有 Map 映射關係的 Set 集合。通常,返回的 Set 又將在 AbstractSet 上實現。此 Set 不應該支持 add() 或 remove() 方法,其迭代器也不應該支持 remove() 方法。
要實現一個可修改的 Map,我們必須另外重寫此類的 put 方法(否則將拋出 UnsupportedOperationException 異常),並且由 entrySet().iterator() 返回的迭代器也必須另外實現其 remove() 方法。
按照 Map 接口規範中的建議,我們通常應該提供一個 void(無參數)構造方法和 map 構造方法。
源碼分析
構造方法
唯一的無參構造函數,用於子類構造函數的調用,通常是隱式的:
protected AbstractMap() {
}
成員變量
其成員變量有如下兩個:
transient Set<K> keySet;
transient Collection<V> values;
都是由 transient 關鍵字所修飾,表示在序列化時不保存。
並且都只是聲明瞭,並沒有被賦值。賦值將會在下面的 keySet() 與 values() 方法中操作。
- keySet:保存所有 key 的 Set 集合。
- values:保存所有 value 的 Collection 集合。
抽象方法
抽象方法就只有一個,返回保存 Map 中所有映射條目的 Set 集合:
public abstract Set<Entry<K,V>> 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<Entry<K,V>> i = entrySet().iterator();
// 循環遍歷取出 value 與 指定的 value 進行比對,比對成功返回 true
if (value==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getValue()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (value.equals(e.getValue()))
return true;
}
}
// 沒有找到,返回 false
return false;
}
返回集合中是否存在指定的 key(支持 null),存在則返回 true,否則返回 false:
public boolean containsKey(Object key) {
// 獲取迭代器
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
// 循環遍歷取出 key 與 指定的 key 進行比對,比對成功返回 true
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return true;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
return true;
}
}
// 沒有找到,返回 false
return false;
}
獲取指定 key 所映射的 value,沒有找到,則返回 null:
public V get(Object key) {
// 獲取迭代器
Iterator<Entry<K,V>> i = entrySet().iterator();
// 循環遍歷,當找到對應的 key 時取出與之相對應的 value
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return e.getValue();
}
} else {
while (i.hasNext()) {
Entry<K,V> 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<Entry<K,V>> i = entrySet().iterator();
// 定義一個映射條目變量,初始化爲 null
Entry<K,V> correctEntry = null;
// 當 correntEntry 沒有被賦值並集合中還有映射條目時
// 就一直循環遍歷集合找出與指定 key 相對應的映射條目
if (key==null) {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
correctEntry = e;
}
} else {
while (correctEntry==null && i.hasNext()) {
Entry<K,V> 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<K> keySet() {
Set<K> ks = keySet;
// 當 key 的視圖集合是空,則創建 AbstractSet 對象然後賦值
if (ks == null) {
ks = new AbstractSet<K>() {
// AbstractSet 是一個抽象類,於是需要實現其抽象方法
public Iterator<K> iterator() {
// 返回的迭代器方法都是調用的 enterySet() 的迭代器實現
return new Iterator<K>() {
private Iterator<Entry<K,V>> 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<V> values() {
// 該方法的實現同 keySet() 方法
// 只不過由 Set 的 AbstractSet 對象實例,變爲了 Collection 的 AbstractCollection 對象實例。
Collection<V> vals = values;
if (vals == null) {
vals = new AbstractCollection<V>() {
public Iterator<V> iterator() {
return new Iterator<V>() {
private Iterator<Entry<K,V>> 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<Entry<K,V>> i = entrySet().iterator();
// 遍歷映射條目
while (i.hasNext()) {
// 獲取 value 與指定 map 對象的 value 進行比較,不相等則返回 false
Entry<K,V> 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<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext())
// AbstractMap 的 hashCode 計算
// 是其每個映射條目的 hashCode 的總和
h += i.next().hashCode();
return h;
}
其他方法
返回當前 Map 的字符串表示形式:
public String toString() {
// 獲取迭代器
Iterator<Entry<K,V>> i = entrySet().iterator();
// 沒有數據時,直接返回:{}
if (! i.hasNext())
return "{}";
// 創建 StringBuilder 對象,用於拼接字符串
StringBuilder sb = new StringBuilder();
// 循環開始前,拼接開始括號: {
sb.append('{');
for (;;) {
// 獲取映射條目的 key、value
Entry<K,V> 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 內部類用的。
用於檢查給定的兩個對象是否相等(包括 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<K,V>, 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<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) {
// 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 抽象類的源碼就看完了。最後再來總結一下。
總結
AbstractMap 作爲實現 Map 接口的抽象類,其作用及目的就是簡化實現 Map 接口所帶來的工作量。像常用的 TreeMap、HashMap 等都是繼承自它。我們在自己設計框架的時候,其實也是可以採用這種設計思想的。