LinkedHashMap<K,V>
繼承自HashMap<K,V>,可知存入的節點key永遠是惟一的。能夠經過Android的LruCache瞭解LinkedHashMap
用法。java
1 public class LinkedHashMap<K,V> 2 extends HashMap<K,V> 3 implements Map<K,V>
Entry<K,V>
是HashMap.Node<K,V>
的子類,增長before
、after
引用實現雙向鏈表git
1 static class Entry<K,V> extends HashMap.Node<K,V> { 2 // 前節點、後節點 3 Entry<K,V> before, after; 4 5 Entry(int hash, K key, V value, Node<K,V> next) { 6 // 調用HashMap構造方法 7 super(hash, key, value, next); 8 } 9 }
雙向鏈表頭,指向最先(最老)訪問節點元素github
transient LinkedHashMap.Entry<K,V> head;
雙向鏈表尾,指向最近(最晚)訪問節點元素app
transient LinkedHashMap.Entry<K,V> tail;
是否保持訪問順序,爲true則每次被訪問的節點都會放到鏈表尾部this
final boolean accessOrder;
依次插入Entry_0
到Entry_5
,當accessOrder
爲true並訪問Entry_4
時,Entry_4
會被移到鏈尾spa
1 public LinkedHashMap(int initialCapacity, float loadFactor) { 2 super(initialCapacity, loadFactor); 3 accessOrder = false; 4 } 5 6 public LinkedHashMap(int initialCapacity) { 7 super(initialCapacity); 8 accessOrder = false; 9 } 10 11 public LinkedHashMap() { 12 super(); 13 accessOrder = false; 14 } 15 16 public LinkedHashMap(Map<? extends K, ? extends V> m) { 17 super(); 18 accessOrder = false; 19 putMapEntries(m, false); 20 } 21 22 // 維持存取順序僅能經過此構造方法 23 public LinkedHashMap(int initialCapacity, 24 float loadFactor, 25 boolean accessOrder) { 26 super(initialCapacity, loadFactor); 27 this.accessOrder = accessOrder; 28 }
1 // 把節點插入到鏈表尾部 2 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { 3 LinkedHashMap.Entry<K,V> last = tail; 4 tail = p; 5 // 若是尾節點爲空代表鏈表沒有元素,則p就是頭結點 6 if (last == null) 7 head = p; 8 else { 9 // 處理雙向鏈表節點 10 p.before = last; 11 last.after = p; 12 } 13 } 14 15 // apply src's links to dst 16 private void transferLinks(LinkedHashMap.Entry<K,V> src, 17 LinkedHashMap.Entry<K,V> dst) { 18 LinkedHashMap.Entry<K,V> b = dst.before = src.before; 19 LinkedHashMap.Entry<K,V> a = dst.after = src.after; 20 if (b == null) 21 head = dst; 22 else 23 b.after = dst; 24 if (a == null) 25 tail = dst; 26 else 27 a.before = dst; 28 } 29 30 // 重寫HashMap鉤子方法 31 void reinitialize() { 32 super.reinitialize(); 33 head = tail = null; 34 } 35 36 // 建立新鏈表節點 37 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { 38 LinkedHashMap.Entry<K,V> p = 39 new LinkedHashMap.Entry<>(hash, key, value, e); 40 linkNodeLast(p); 41 return p; 42 } 43 44 // 替換鏈表節點 45 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { 46 LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; 47 LinkedHashMap.Entry<K,V> t = 48 new LinkedHashMap.Entry<>(q.hash, q.key, q.value, next); 49 transferLinks(q, t); 50 return t; 51 } 52 53 // 建立新紅黑樹節點 54 TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) { 55 TreeNode<K,V> p = new TreeNode<>(hash, key, value, next); 56 linkNodeLast(p); 57 return p; 58 } 59 60 // 替換紅黑樹節點 61 TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { 62 LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; 63 TreeNode<K,V> t = new TreeNode<>(q.hash, q.key, q.value, next); 64 transferLinks(q, t); 65 return t; 66 }
1 // 把節點從鏈表解除連接 2 void afterNodeRemoval(Node<K,V> e) { 3 // p:便是節點e 4 // b:e的前一個節點 5 // a:e的後一個節點 6 LinkedHashMap.Entry<K,V> p = 7 (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; 8 9 // 置空節點p的先後引用 10 p.before = p.after = null; 11 12 if (b == null) { 13 // 可知節點e是鏈表頭結點,則e的下一個節點a做爲鏈表的頭結點 14 head = a; 15 } else { 16 // 可知節點e本是中間結點,把e下一個節點a做爲e上一個節點的後續節點 17 b.after = a; 18 } 19 20 if (a == null) { 21 // 可知節點e本是鏈表尾節點,則e的上一個節點b做爲鏈表的尾節點 22 tail = b; 23 } else { 24 // 可知節點e自己是中間節點,把e上一個節點b做爲e下一個節點的前置節點 25 a.before = b; 26 } 27 } 28 29 // 父類HashMap調用putVal()中會調用此方法,移除最少使用的節點 30 void afterNodeInsertion(boolean evict) { 31 // first是最少使用的節點 32 LinkedHashMap.Entry<K,V> first; 33 if (evict && (first = head) != null && removeEldestEntry(first)) { 34 // 獲取first的key 35 K key = first.key; 36 // 經過key找到對應Node並移除 37 removeNode(hash(key), key, null, false, true); 38 } 39 } 40 41 // 把節點移動到鏈表尾 42 void afterNodeAccess(Node<K,V> e) { 43 LinkedHashMap.Entry<K,V> last; 44 // 僅當accessOrder爲true且被訪問元素不是尾節點 45 if (accessOrder && (last = tail) != e) { 46 // p:便是節點e 47 // b:e的前一個節點 48 // a:e的後一個節點 49 LinkedHashMap.Entry<K,V> p = 50 (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; 51 52 // 置空節點p的後引用 53 p.after = null; 54 55 if (b == null) { 56 // 可知節點e本是頭結點,則e的下一個節點a做爲鏈表的頭結點 57 head = a; 58 } else { 59 // 可知節點e本是中間結點,把e下一個節點a做爲e上一個節點的後續節點 60 b.after = a; 61 } 62 63 if (a != null) { 64 // 可知節點e自己是中間節點,把e上一個節點b做爲e下一個節點的前置節點 65 a.before = b; 66 } else { 67 // 可知節點e本是尾節點,則e的上一個節點b做爲鏈表的尾節點 68 last = b; 69 } 70 71 // p以前沒有節點,代表p就是頭結點 72 if (last == null) { 73 head = p; 74 } else { 75 // p做爲新的尾節點,連接到上一個尾節點以後 76 p.before = last; 77 last.after = p; 78 } 79 80 tail = p; // tail引用指向p 81 ++modCount; // 修改次數遞增 82 } 83 }
1 // 檢查LinkedHashMap是否包含指定value 2 public boolean containsValue(Object value) { 3 // 從鏈表頭結點開始遍歷,逐個查找Entry.value是否等於value 4 for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { 5 V v = e.value; 6 if (v == value || (value != null && value.equals(v))) 7 return true; // 包含對應value,返回true 8 } 9 return false; // 不包含對應value,返回false 10 } 11 12 // 經過Key獲取對應Entry的value 13 public V get(Object key) { 14 Node<K,V> e; 15 if ((e = getNode(hash(key), key)) == null) { 16 // 經過key獲取Node爲空,返回null做爲結果 17 return null; 18 } 19 20 if (accessOrder) { 21 // 經過key獲取Node不爲空,執行afterNodeAccess(e)調整順序 22 afterNodeAccess(e); 23 } 24 25 return e.value; // 最後把獲取的Entry.value返回 26 } 27 28 // 經過Key獲取對應Entry的value 29 public V getOrDefault(Object key, V defaultValue) { 30 Node<K,V> e; 31 if ((e = getNode(hash(key), key)) == null) { 32 // 經過key獲取Node爲空,返回defaultValue做爲結果 33 return defaultValue; 34 } 35 36 if (accessOrder) { 37 // 經過key獲取Node不爲空,執行afterNodeAccess(e)調整順序 38 afterNodeAccess(e); 39 } 40 41 return e.value; // 最後把獲取的Entry.value返回 42 }
1 // 清除全部引用 2 public void clear() { 3 super.clear(); // 把HashMap全部Entry都清空 4 head = tail = null; // 置空head和tail引用 5 } 6 7 // 方法主要用於被子類重寫,決定最少使用的節點可否被移除 8 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { 9 return false; 10 }