類的定義
public class LinkedHashMap<K, V> extends HashMap<K, V> {}
- 基於雙向鏈表實現,屬於Map的一類,其父類是HashMap。最主要的是LinkedHashMap可以保證迭代順序。如果既想要使用Map的功能又想要鍵值對插入和取出是有序的,可以使用LinkedHashMap
- 鍵和值支持任意類型,包括null
- 迭代的順序就是鍵值對被插入Map的順序。如果三個參數的構造方法中accessOrder參數被設置爲true,那麼鍵值對的迭代順序就變爲訪問順序(可以影響訪問的操作是put get putAll),在Android的LRUCache中使用的數據結構就是LinkedHashMap。
- 非線程安全的,如果同時有多個線程執行改變Map結構的操作如put或者remove等操作的時候就需要調用者保證線程安全性
- 迭代器進行迭代的時候同樣是不允許有修改Map結構的操作,否則拋出
ConcurrentModificationException
主要成員變量
// true的時候按訪問順序排列鍵值對,false的時候按插入順序
private final boolean accessOrder;
transient LinkedEntry<K, V> header;
構造函數
public LinkedHashMap() {
init();
accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public LinkedHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, false);
}
public LinkedHashMap(
int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
init();
this.accessOrder = accessOrder;
}
public LinkedHashMap(Map<? extends K, ? extends V> map) {
this(capacityForInitSize(map.size()));
constructorPutAll(map);
}
構造函數和前面的集合類差不多,只是這裏多了一個訪問順序的標誌位
鍵值對實體
static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
LinkedEntry<K, V> nxt;
LinkedEntry<K, V> prv;
/** Create the header entry */
LinkedEntry() {
super(null, null, 0, null);
nxt = prv = this;
}
/** Create a normal entry */
LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
super(key, value, hash, next);
this.nxt = nxt;
this.prv = prv;
}
}
在HashMap鍵值對的實體上增加了nxt prv指針。除此之外在構造函數中調用了super();方法,該方法在HashMap中用於將映射實體加入到數組中。
返回最早加入的實體
public Entry<K, V> eldest() {
LinkedEntry<K, V> eldest = header.nxt;
return eldest != header ? eldest : null;
}
如果Map爲空,那麼返回NULL
添加新的映射實體
@Override void addNewEntry(K key, V value, int hash, int index) {
LinkedEntry<K, V> header = this.header;
// Remove eldest entry if instructed to do so.
LinkedEntry<K, V> eldest = header.nxt;
if (eldest != header && removeEldestEntry(eldest)) {
remove(eldest.key);
}
// Create new entry, link it on to list, and put it into table
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
key, value, hash, table[index], header, oldTail);
table[index] = oldTail.nxt = header.prv = newTail;
}
這個方法在LruCache中會用到,每當添加一個新的實體的時候就刪除最久的那個,是否刪除通過removeEldestEntry(eldest)
來判斷,該方法返回true 或者false。方法的後半部分是一個雙向鏈表的增加節點操作,頭節點的nxt指向第一個節點,prv指向最後一個節點。添加新節點的時候主要是修改prv和prv所指向的節點的指針。
添加NULL key的映射實體
@Override void addNewEntryForNullKey(V value) {
LinkedEntry<K, V> header = this.header;
// Remove eldest entry if instructed to do so.
LinkedEntry<K, V> eldest = header.nxt;
if (eldest != header && removeEldestEntry(eldest)) {
remove(eldest.key);
}
// Create new entry, link it on to list, and put it into table
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail = new LinkedEntry<K,V>(
null, value, 0, null, header, oldTail);
entryForNullKey = oldTail.nxt = header.prv = newTail;
}
前半部分和上面的添加操作一樣,後面就是倒數第二句不同,沒有設置key和hash,以及table[]
只添加不移除
@Override HashMapEntry<K, V> constructorNewEntry(
K key, V value, int hash, HashMapEntry<K, V> next) {
LinkedEntry<K, V> header = this.header;
LinkedEntry<K, V> oldTail = header.prv;
LinkedEntry<K, V> newTail
= new LinkedEntry<K,V>(key, value, hash, next, header, oldTail);
return oldTail.nxt = header.prv = newTail;
}
功能和上面的兩個方法差不多,只沒有判斷是夠進行移除操作
根據給定的key返回指定值
@Override public V get(Object key) {
/*
* This method is overridden to eliminate the need for a polymorphic
* invocation in superclass at the expense of code duplication.
*/
if (key == null) {
HashMapEntry<K, V> e = entryForNullKey;
if (e == null)
return null;
if (accessOrder)
makeTail((LinkedEntry<K, V>) e);
return e.value;
}
int hash = Collections.secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
e != null; e = e.next) {
K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
if (accessOrder)
makeTail((LinkedEntry<K, V>) e);
return e.value;
}
}
return null;
}
private void makeTail(LinkedEntry<K, V> e) {
// Unlink e
e.prv.nxt = e.nxt;
e.nxt.prv = e.prv;
// Relink e as tail
LinkedEntry<K, V> header = this.header;
LinkedEntry<K, V> oldTail = header.prv;
e.nxt = header;
e.prv = oldTail;
oldTail.nxt = header.prv = e;
modCount++;
}
這部分代碼和父類HashMap有些重複,但是優點是減少了由於多態導致的父類方法調用。代碼中的entryForNullKey
是父類HahMap中的成員變量,如果沒有NULL key的映射,那麼entryForNullKey也是NULL。
代碼中使用的makeTail()的作用是將給定的映射實體從雙向鏈表中取出,然後添加到鏈表尾部,在訪問操作或者刪除操作過程中,如果accessOrder
爲true,也就是需要按訪問順序進行排序的時候,都要調用MakeTail方法,將指定節點移動到尾部。
get()方法的後半部分看起來可能有一些奇怪,不是說LinkedHashMap是基於雙向循環鏈表嗎,爲什麼還有數組的迭代操作??實際上LinkedHashMap是數組和雙向鏈表的結合,HashMap基於數組,LinkedHashMap是其子類,在HashMap的基礎上增加了兩個指針,除了將映射實體加入到數組中之外,還額外維護了兩個指針。這樣理解的話,get()方法的後半部分就比較好理解了,直接是按key的Hash值對數組進行遍歷,如果找到對應的映射,則返回值,否則返回Null
刪除指定節點
@Override void postRemove(HashMapEntry<K, V> e) {
LinkedEntry<K, V> le = (LinkedEntry<K, V>) e;
le.prv.nxt = le.nxt;
le.nxt.prv = le.prv;
le.nxt = le.prv = null; // Help the GC (for performance)
}
判斷是否包含
@Override public boolean containsValue(Object value) {
if (value == null) {
for (LinkedEntry<K, V> header = this.header, e = header.nxt;
e != header; e = e.nxt) {
if (e.value == null) {
return true;
}
}
return false;
}
// value is non-null
for (LinkedEntry<K, V> header = this.header, e = header.nxt;
e != header; e = e.nxt) {
if (value.equals(e.value)) {
return true;
}
}
return false;
}
由於LinkedHashMap繼承於HashMap,支持null key 和null value,所以需要判斷key是否爲空。在父類HashMap中已經有了containsValue()方法了,這裏的重載是基於鏈表的查找,這樣可以減少迭代的開銷。
清空集合
public void clear() {
super.clear();
// Clear all links to help GC
LinkedEntry<K, V> header = this.header;
for (LinkedEntry<K, V> e = header.nxt; e != header; ) {
LinkedEntry<K, V> nxt = e.nxt;
e.nxt = e.prv = null;
e = nxt;
}
header.nxt = header.prv = header;
}
清空操作不僅僅是清空鏈表間的指針鏈,還需要調用父類的clear()清空映射實體