本文內容:java
LinkedHashMap概述數組
LinkedHashMap源碼分析緩存
LinkedHashMap概述性能優化
LinkedHashMap相似 於HashMap,區別在於採用迭代器迭代每一個元素時,其順序是按照插入次序或者是LRU次序。
ide
其繼承 關係以下:函數
LinkedHashMap直接繼承了HashMap,實現了Map接口。源碼分析
LinkedHashMap用Hash存儲全部元素,但迭代時卻又能夠按順序遍歷,這是如何作到的呢?性能
LinkedHashMap源碼分析優化
看一下LinkedHashMap的屬性this
/** * The head of the doubly linked list. */ private transient Entry<K,V> header; /** * The iteration ordering method for this linked hash map: <tt>true</tt> * for access-order, <tt>false</tt> for insertion-order. * * @serial */ private final boolean accessOrder;
其中header列表記錄着元素的插入或者LRU次序,因此很明顯,LinkedHashMap可以迭代器順序訪問必定和這個header有關。其實確實是的,header是一個循環雙向鏈表!accessOrder則用來標記是按插入順序仍是訪問順序。
構造函數:
public LinkedHashMap() { super(); accessOrder = false; } public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
調用了HashMap的構造函數,在HashMap的構造函數中調用了init()方法
@Override void init() { header = new Entry<>(-1, null, null, null); header.before = header.after = header; }
init是初始化header鏈表。header鏈表是Entry類型,LinkedHashMap的一個內部靜態類,繼承了HashMap.Entry。
private static class Entry<K,V> extends HashMap.Entry<K,V> { // These fields comprise the doubly linked list used for iteration. Entry<K,V> before, after; Entry(int hash, K key, V value, HashMap.Entry<K,V> next) { super(hash, key, value, next); } //省略 }
看LinkedHashMap,大部分都是重寫(override)父類的一些方法。
/** * This override differs from addEntry in that it doesn't resize the * table or remove the eldest entry. */ void createEntry(int hash, K key, V value, int bucketIndex) { HashMap.Entry<K,V> old = table[bucketIndex]; Entry<K,V> e = new Entry<>(hash, key, value, old); table[bucketIndex] = e; e.addBefore(header); size++; }
createEntry不只把元素添加進了table數組,還經過addBefore將其添加進header鏈表。
private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; }
在header前面插入該元素。最後造成這樣的鏈表:
這個鏈表在迭代器中用。
void addEntry(int hash, K key, V value, int bucketIndex) { super.addEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed Entry<K,V> eldest = header.after; if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } } protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }
這裏,會調用removeEntryForKey去除最老的元素,但removeEldestEntry方法返回的是false,這樣不是永遠不會執行removeEntryForKey嗎?是的。
這樣作是爲了讓LinkedHashMap的行爲是符合正常的Map,不會由於增長元素而把舊的元素給去除。
那直接忽略不就好了嗎?
看註釋,若是咱們想實現一個cache,只須要重載LinkedHashMap,而後重寫removeEldestEntry就能夠完成簡單的,限制大小的LRU緩存了。好比實現一個100元素的cache,能夠這樣寫:
private static final int MAX_ENTRIES = 100; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; }
再看一下transfer
@Override void transfer(HashMap.Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e = header.after; e != header; e = e.after) { if (rehash) e.hash = (e.key == null) ? 0 : hash(e.key); int index = indexFor(e.hash, newCapacity); e.next = newTable[index]; newTable[index] = e; } }
這個方法被父類的resize調用,重寫它是爲了實現性能優化!爲何呢?由於這裏直接用了header鏈表,而HashMap是遍歷全部的table槽。
public boolean containsValue(Object value) { // Overridden to take advantage of faster iterator if (value==null) { for (Entry e = header.after; e != header; e = e.after) if (e.value==null) return true; } else { for (Entry e = header.after; e != header; e = e.after) if (value.equals(e.value)) return true; } return false; }
遍歷header鏈表,查看是否包含該value.
public V get(Object key) { Entry<K,V> e = (Entry<K,V>)getEntry(key); if (e == null) return null; e.recordAccess(this); return e.value; }
和HashMap方法相比,多了recordAccess方法的調用。recordAccess方法在Entry中實現。
void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } } private void remove() { before.after = after; after.before = before; }
由代碼可知,若是accessOrder被設置成true,就能夠有LRU的功能。
最後還剩下iterator方法沒看,咱們說LinkedHashMap是支持按插入順序或者LRU順序的,說的是遍歷的時候。它的實現方法就是遍歷header鏈表。
把LinkedHashMap的迭代器所有貼出來,和HashMap差很少:
private class KeyIterator extends LinkedHashIterator<K> { public K next() { return nextEntry().getKey(); } } private class ValueIterator extends LinkedHashIterator<V> { public V next() { return nextEntry().value; } } private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } // These Overrides alter the behavior of superclass view iterator() methods Iterator<K> newKeyIterator() { return new KeyIterator(); } Iterator<V> newValueIterator() { return new ValueIterator(); } Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
它們都繼承了LinkedHashIterator,LinkedHashIterator是LinkedHashMap的內部抽象類,繼承Iterator
private abstract class LinkedHashIterator<T> implements Iterator<T>
LinkedHashIterator的屬性以下:
Entry<K,V> nextEntry = header.after; Entry<K,V> lastReturned = null; int expectedModCount = modCount;
hasNext方法:
public boolean hasNext() { return nextEntry != header; }
remove方法
public void remove() { if (lastReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); LinkedHashMap.this.remove(lastReturned.key); lastReturned = null; expectedModCount = modCount; }
nextEntry方法
Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextEntry == header) throw new NoSuchElementException(); Entry<K,V> e = lastReturned = nextEntry; nextEntry = e.after; return e; }
iterator的實現思路和HashMap很類似,代碼找着header鏈表的結構就很容易理解了。
總結
LinkedHashMap使用hash來存放元素,同時也將該元素存放在header鏈表中,這樣,就能夠hash方式get/put元素,List方式迭代元素。