LinkedHashMap繼承了HashMap,他在HashMap的基礎上增長了一個雙向鏈表的結構,鏈表默認維持key插入的順序,重複的key值插入不會改變順序,適用於使用者須要返回一個順序相同的map對象的狀況。還能夠生成access-order順序的版本,按照最近訪問順序來存儲,剛被訪問的結點處於鏈表的末尾,適合LRU,put get compute merge都算做一次訪問,其中put key值相同的結點也算做一次訪問,replace只有在換掉一個鍵值對的時候纔算一次訪問,putAll產生的訪問順序取決於本來map的迭代器實現。安全
在插入鍵值對時,能夠經過對removeEldestEntry重寫來實現新鍵值對插入時自動刪除最舊的鍵值對函數
擁有HashMap提供的方法,迭代器由於是經過遍歷雙向鏈表,因此額外開銷與size成正比與capacity無關,所以選擇過大的初始大小對於遍歷時間的增長沒有HashMap嚴重,後者的遍歷時間依賴與capacity。spa
一樣是非線程安全方法,對於LinkedHashMap來講,修改結構的操做除了增長和刪除鍵值對外,還有對於access-order時進行了access致使迭代器順序改變,主要是get操做,對於插入順序的來講,僅僅修改一個已有key值的value值不是一個修改結構的操做,但對於訪問順序,put和get已有的key值會改變順序。迭代器也是fail-fast設計,可是fail-fast只是一個調試功能,一個設計良好的程序不該該出現這個錯誤線程
由於HashMap加入了TreeNode,因此如今LinkedHashMap也有這個功能設計
如下描述中的鏈表,若無特別說明都是指LinkedHashMap的雙向鏈表指針
先來看一下基本結構,每一個鍵值對加入了先後指針,集合加入了頭尾指針來造成雙向鏈表,accessOrder表明鏈表是以訪問順序仍是插入順序存儲調試
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); } } /** * The head (eldest) of the doubly linked list.頭部 */ transient LinkedHashMap.Entry<K,V> head; /** * The tail (youngest) of the doubly linked list.尾部 */ transient LinkedHashMap.Entry<K,V> tail; //true訪問順序 false插入順序 final boolean accessOrder;
而後是幾個內部方法。linkNodeLast將p鏈接到鏈表尾部code
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p;//本來鏈表爲空則p同時爲頭部 else { p.before = last; last.after = p; } }
transferLinks用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; }
reinitialize在調用HashMap方法的基礎上,將head和tail設爲nullblog
void reinitialize() { super.reinitialize(); head = tail = null; }
newNode生成一個LinkedHashMap結點,next指向e,插入到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);//新建一個鍵值對,next指向e linkNodeLast(p);//p插入到LinkedHashMap鏈表末端 return p; }
replacementNode根據原結點生成一個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);//生成一個新的鍵值對next是給出的next參數 transferLinks(q, t);//用t替換q return t; }
newTreeNode生成一個TreeNode結點,next指向next,插入到LinkedHashMap鏈表末端
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) { TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);//生成一個TreeNode,next指向參數next linkNodeLast(p);//p插入到LinkedHashMap鏈表末端 return p; }
replacementTreeNode根據結點p生成一個新的TreeNode,next設爲給定的next,替換本來的p
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next); transferLinks(q, t);//根據結點p生成一個新的TreeNode,next設爲給定的next,替換本來的p return t; }
afterNodeRemoval從LinkedHashMap的鏈上移除結點e
void afterNodeRemoval(Node<K,V> e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.before = p.after = null; if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; }
afterNodeInsertion可能移除最舊的結點,須要evict爲true同時鏈表不爲空同時removeEldestEntry須要重寫
void afterNodeInsertion(boolean evict) { LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) {//removeEldestEntry須要重寫才從發揮做用,不然必定返回false K key = first.key;//移除鏈表頭部的結點 removeNode(hash(key), key, null, false, true); } }
afterNodeAccess在訪問事後將結點e移動到鏈表尾部,須要Map是access-order,若移動成功則增長modCount
void afterNodeAccess(Node<K,V> e) { LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) {//Map是access-order同時e不是鏈表的尾部 LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null)//將結點e從鏈表中剪下 head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p;//結點e移動到鏈表尾部 ++modCount;//由於有access-order下結點被移動,因此增長modCount } }
構造函數方面,accessOrder默認是false插入順序,初始大小爲16,負載因子爲0.75,這裏是同HashMap。複製構造也是調用了HashMap.putMapEntries方法
containsValue遍歷鏈表尋找相等的value值,這個操做必定不會形成結構改變
public boolean containsValue(Object value) { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {//檢查一樣是根據LinkedHashMap提供的鏈表順序進行遍歷 V v = e.value; if (v == value || (value != null && value.equals(v))) return true; } return false; }
get方法複用HashMap的getNode方法,若找到結點且Map是訪問順序時,要將訪問的結點放到鏈表最後,若沒找到則返回null。而getOrDefault僅有的區別是沒找到時返回defaultValue
public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null)//複用HashMap的getNode方法 return null; if (accessOrder) afterNodeAccess(e);//access-order時將e放到隊尾 return e.value; } public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return defaultValue;//複用HashMap的getNode方法,若沒有找到對應的結點則返回defaultValue if (accessOrder) afterNodeAccess(e);//access-order時將e放到隊尾 return e.value; }
clear方法在HashMap的基礎上要把head和tail設爲null
public void clear() { super.clear(); head = tail = null; }
removeEldestEntry在put和putAll插入鍵值對時調用,本來是必定返回false的,若是要自動刪除最舊的鍵值對要返回true,須要進行重寫。好比下面這個例子,控制size不能超過100
private static final int MAX_ENTRIES = 100; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; }
下面兩個方法和HashMap類似,返回key的Set和value的Collection還有返回鍵值對的Set,這個是直接引用,因此對它們的remove之類的修改會直接反饋到LinkedHashMap上
public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new LinkedKeySet(); keySet = ks; } return ks;//返回key值的set } public Collection<V> values() { Collection<V> vs = values; if (vs == null) { vs = new LinkedValues(); values = vs; } return vs;//返回一個包含全部value值的Collection } public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;//返回一個含有全部鍵值對的Set }
檢查HashMap的putVal方法,咱們能夠看到在找到了相同key值並修改value值時會調用afterNodeAccess,對於access-order會改變結點順序
if (e != null) { // 找到了相同的key則修改value值並返回舊的value V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; }