LinkedHashMap
是HashMap
的子類,因此也具有HashMap
的諸多特性。不一樣的是,LinkedHashMap
還維護了一個雙向鏈表,以保證經過Iterator
遍歷時順序與插入順序一致。除此以外,它還支持Access Order,即按照元素被訪問的順序來排序,咱們熟知的LRUCache
底層就依賴於此。如下是文檔中須要咱們注意的點:java
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map.node
A special LinkedHashMap(int,float,boolean) constructor is provided to create a linked hash map whose order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently (access-order). This kind of map is well-suited to building LRU caches.數組
The removeEldestEntry(Map.Entry) method may be overridden to impose a policy for removing stale mappings automatically when new mappings are added to the map.app
Note, however, that the penalty for choosing an excessively high value for initial capacity is less severe for this class than for HashMap, as iteration times for this class are unaffected by capacity.less
下面咱們就從構造函數和成員變量開始分析其具體實現。ide
在分析成員變量前,咱們先看下其存儲元素的結構。函數
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }
這個Entry
在HashMap
中被引用過,主要是爲了能讓LinkedHashMap
也支持樹化。在這裏則是用來存儲元素。學習
// 雙向鏈表的頭,用做AccessOrder時也是最老的元素 transient LinkedHashMap.Entry<K,V> head; // 雙向鏈表的尾,用做AccessOrder時也是最新的元素 transient LinkedHashMap.Entry<K,V> tail; // true則爲訪問順序,false則爲插入順序 final boolean accessOrder;
關於LinkedHashMap
的構造函數咱們只關注一個,其餘的都和HashMap
相似,只是把accessOrder
設置爲了false。在上邊的文檔說過,initialCapacity並無在HashMap
中那般重要,由於鏈表不須要像數組那樣必須先聲明足夠的空間。下面這個構造函數是支持訪問順序的。ui
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
LinkedHashMap
並無再實現一整套增刪改查的方法,而是經過複寫HashMap
在此過程當中定義的幾個方法來實現的。對此不熟悉的能夠查看文末關於HashMap
分析的文章,或者對照HashMap
的源碼來看。this
HashMap
在插入時,調用了newNode
來新建一個節點,或者是經過replacementNode
來替換值。在樹化時也有兩個對應的方法,分別是newTreeNode
和replacementTreeNode
。完成以後,還調用了afterNodeInsertion
方法,這個方法容許咱們在插入完成後作些事情,默認是空實現。
爲了方便分析,咱們會對比HashMap
中的實現與LinkedHashMap
的實現,來摸清它是如何作的。
// HashMap中的實現 Node<K, V> newNode(int hash, K key, V value, Node<K, V> next) { return new Node<>(hash, key, value, next); } // LinkedHashMap中的實現 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e); linkNodeLast(p); return p; } // HashMap中的實現 Node<K, V> replacementNode(Node<K, V> p, Node<K, V> next) { return new Node<>(p.hash, p.key, p.value, next); } // LinkedHashMap中的實現 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; LinkedHashMap.Entry<K,V> t = new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next); transferLinks(q, t); return t; } // newTreeNode和replacementTreeNode和此相似
經過以上對比,能夠發現,LinkedHashMap
在新增時,調用了linkNodeLast
,再替換時調用了transferLinks
。如下是這兩個方法的實現。
// 就是將元素掛在鏈尾 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; } } // 用dst替換src private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst) { LinkedHashMap.Entry<K,V> b = dst.before = src.before; LinkedHashMap.Entry<K,V> a = dst.after = src.after; if (b == null) head = dst; else b.after = dst; if (a == null) tail = dst; else a.before = dst; }
最後咱們看下afterNodeInsertion
作了哪些事情吧:
// evict在HashMap中說過,爲false表示是建立階段 void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; // 不是建立階段 if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; // 自動刪除最老的元素,也就是head元素 removeNode(hash(key), key, null, false, true); } }
removeEldestEntry
是當想要在插入元素時自動刪除最老的元素時須要複寫的方法。其默認實現以下:
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }
由於要支持訪問順序,因此獲取元素的方法和HashMap
也有所不一樣。下面咱們看下其實現:
public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) // 數據被訪問,須要將其移動到末尾 afterNodeAccess(e); return e.value; }
getNode
方法是在HashMap
中實現的,因此這是包裝了一下HashMap
的方法,並添加了一個afterNodeAccess
,其實現以下:
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; // e元素不在末尾 if (accessOrder && (last = tail) != e) { // p是e,b是前一個元素,a是後一個元素 LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; // e要放在末尾,因此沒有after p.after = null; // 把e去掉,把b和a接起來 if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; //把e接在末尾 if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
關於LinkedHashMap
的分析就到這裏了,其餘關於Iterator
的內容都和Collection
是大同小異的,感興趣的能夠去查看相關源碼。
【感謝您能看完,若是可以幫到您,麻煩點個贊~】
更多經驗技術歡迎前來共同窗習交流: 一點課堂-爲夢想而奮鬥的在線學習平臺 http://www.yidiankt.com/
![關注公衆號,回覆「1」免費領取-【java核心知識點】]
QQ討論羣:616683098
QQ:3184402434
想要深刻學習的同窗們能夠加我QQ一塊兒學習討論~還有全套資源分享,經驗探討,等你哦!