Map集合是用來存儲<Key, Value>鍵值對數據的,是平常開發中使用最多的數據結構之一。Map集合相對List集合來講結構會稍微複雜一些,因此Map系列會分開寫。本文主要分析AbstractMap。html
整個AbstractMap類中只有一個抽象方法:java
public abstract Set<Entry<K,V>> entrySet();
複製代碼
也就是說全部的子類都必須實現entrySet()
方法。縱觀AbstractMap中的成員方法內部實現,基本都依賴於entrySet()
方法,它返回了Map所保存的鍵值對。api
AbstractMap有個默認拋UnsupportedOperationException
異常的方法:安全
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
複製代碼
整個put方法直接影響了:bash
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
複製代碼
也就是說Map默認是不支持修改的,子類若是想實現可變的Map,則須要重寫put方法。
有put就應該有remove,來看看remove方法:微信
public V remove(Object key) {
// 使用到了entrySet()獲取保存的數據
Iterator<Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
// 經過迭代器找到要remove的值
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;
if (correctEntry !=null) {
oldValue = correctEntry.getValue();
i.remove(); // 調用迭代器的remove方法
}
return oldValue;
}
複製代碼
Map的remove方法使用到了entrySet()所返回Set的迭代器的remove方法。數據結構
因此若是你想實現本身的Map結構:
1.當要實現一個不可變的Map時,須要繼承AbstractMap,而後實現entrySet() 方法,這個方法返回一個保存全部key-value映射的Set。一般這個Set不支持add()和remove() 方法,Set對應的迭代器也不支持remove()方法。
2. 當要實現一個可變的 Map時,須要在上述操做外,重寫put()方法,並且entrySet()返回的Set 的迭代器須要實現remove()方法。oracle
AbstractMap只有兩個成員變量:源碼分析
transient Set<K> keySet; // 不可序列化
transient Collection<V> values; // 不可序列化
複製代碼
注意:從jdk1.8開始,這兩個變量再也不使用volatile
修飾,由於調用這兩個變量的方法不是同步的,增長volatile
也不能保證線程安全。(本文用的是jdk11)this
這裏看一下怎麼獲取keySet:
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
// 自定義一個Set而且實現迭代器
ks = new AbstractSet<K>() {
public Iterator<K> iterator() {
return new Iterator<K>() {
// 使用Entry的Set集合的迭代器
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();
}
};
}
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;
}
return ks;
}
複製代碼
這裏寫的很巧妙,沒用採用遍歷Entry的方式,而是實現了一個自定義Set集合。這個集合再重寫iterator
方法,直接調用Entry集合的迭代器。values
也作了一樣的處理。
AbstractMap有兩個內部類SimpleEntry<K,V>
和SimpleImmutableEntry<K,V>
,它們都實現了Entry<K,V>
和Serializable
。
SimpleEntry:表示值可變的鍵值對。
private final K key; // 不可變
private V value;
複製代碼
提供了相應的setValue
方法:
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue; // 注意返回的是舊值
}
複製代碼
來看看equals
方法:
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());
}
private static boolean eq(Object o1, Object o2) {
// 由於實際中o1極可能是String類型,因此這裏使用了equals,而不是==
return o1 == null ? o2 == null : o1.equals(o2);
}
複製代碼
SimpleImmutableEntry:表示不可變的鍵值對。
private final K key; // 不可變
private final V value; // 不可變
複製代碼
它的setValue
方法直接拋出異常:
public V setValue(V value) {
throw new UnsupportedOperationException();
}
複製代碼
entrySet()
,子類必須實現;