最近正准備回顧一下Java,所以在此做一些記錄。
LinkedHashMap繼承了HashMap,大多數的操作調用的是HashMap的實現,在進行操作的時候多維護了一層雙向鏈表
LinkedHashMap的節點也繼承了HashMap的節點,多維護了前置節點和后置節點兩個屬性

1 static class Entry<K,V> extends HashMap.Node<K,V> { 2 Entry<K,V> before, after; 3 Entry(int hash, K key, V value, Node<K,V> next) { 4 super(hash, key, value, next); 5 } 6 }
1.put(K key, V value) 存放一個鍵值對,其實是調用了HashMap的put方法,通過重寫HashMap里的部分方法來實現鏈表的維護

1 HashMap的put方法會生成一個節點,調用了newNode方法,而LinkedHashMap重寫了此方法 2 /** 3 * 創建一個節點 4 * @param hash hash值 5 * @param key 鍵 6 * @param value 值 7 * @param e 下一個節點,這個是HashMap節點的屬性 8 * @return 9 */ 10 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { 11 //調用構造方法 12 LinkedHashMap.Entry<K,V> p = 13 new LinkedHashMap.Entry<K,V>(hash, key, value, e); 14 //維護鏈表 15 linkNodeLast(p); 16 return p; 17 } 18 19 /** 20 * 添加一個節點到末尾 21 * @param p 節點 22 */ 23 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { 24 //保存尾部節點 25 LinkedHashMap.Entry<K,V> last = tail; 26 //更新尾部節點 27 tail = p; 28 //判斷之前的尾部節點是否為空 29 if (last == null) 30 //之前的尾部節點為空,說明還沒有數據,設置一下頭節點 31 head = p; 32 else { 33 //說明之前已經有數據了,將新的節點作為尾部節點連接起來 34 p.before = last; 35 last.after = p; 36 } 37 } 38 39 HashMap當put一個已經存在的key時,會觸發是否更新的操作,之后會調用afterNodeAccess方法,LinkedHashMap重寫了此方法 40 /** 41 * accessOrder為true時,將操作的節點移到鏈表尾部 42 * @param e 節點 43 */ 44 void afterNodeAccess(Node<K,V> e) { 45 LinkedHashMap.Entry<K,V> last; 46 //accessOrder 這個參數是指在進行操作的時候,是否將操作的節點移動到鏈表的最后,默認false 47 //也就是說accessOrder為false的時候鏈表就是按照插入順序維護的 48 //true的時候,會將最近使用的節點移動到鏈表最后 49 if (accessOrder && (last = tail) != e) { 50 //保存當前節點和其前置節點和后置節點 51 LinkedHashMap.Entry<K,V> p = 52 (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; 53 //清空后置節點,因為當前節點要被移動到最后了 54 p.after = null; 55 //判斷前置節點是否為空節點 56 if (b == null) 57 //前置節點為空,說明當前節點是頭節點,將它的后置節點也就是第二個節點設置為頭節點 58 head = a; 59 else 60 //存在前置節點,將前置節點的后置節點連接到當前節點的下一個節點 61 b.after = a; 62 //判斷后置節點是否為空 63 if (a != null) 64 //后置節點不為空,更新后置節點的前置節點 65 a.before = b; 66 else 67 //說明該節點就是尾部節點,設置前置節點為后節點 68 //a == null 說明p就是尾部節點? 有點不清楚 69 last = b; 70 //統一更新尾部節點 71 if (last == null) 72 //說明只有這么一個節點 73 head = p; 74 else { 75 //將當前節點掛到鏈表末尾 76 p.before = last; 77 last.after = p; 78 } 79 //設置尾部節點 80 tail = p; 81 ++modCount; 82 } 83 } 84 85 LinkedHashMap也重寫了afterNodeInsertion方法 86 void afterNodeInsertion(boolean evict) { 87 LinkedHashMap.Entry<K,V> first; 88 if (evict && (first = head) != null && removeEldestEntry(first)) { 89 K key = first.key; 90 removeNode(hash(key), key, null, false, true); 91 } 92 } 93 94 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { 95 return false; 96 } 97 98 其實LinkedHashMap中這個方法並不生效
2.get(Object key) 根據key獲取值

1 /** 2 * 獲取值 3 * @param key 鍵 4 * @return 5 */ 6 public V get(Object key) { 7 Node<K,V> e; 8 //調用了HashMap中的getNode方法 9 if ((e = getNode(hash(key), key)) == null) 10 return null; 11 if (accessOrder) 12 //移動當前操作的節點到鏈表最后 13 afterNodeAccess(e); 14 return e.value; 15 }
3. containsValue(Object value) 是否存在某個值

1 public boolean containsValue(Object value) { 2 //通過遍歷鏈表實現 3 for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { 4 V v = e.value; 5 if (v == value || (value != null && value.equals(v))) 6 return true; 7 } 8 return false; 9 }
4.remove(Object key) 刪除key

1 LinkedHashMap調用了HashMap的remove方法 2 重寫了afterNodeRemoval方法 3 /** 4 * 刪除鏈表中的節點 5 * @param e 6 */ 7 void afterNodeRemoval(Node<K,V> e) { 8 //獲取當前節點的前置后置節點 9 LinkedHashMap.Entry<K,V> p = 10 (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; 11 //清空前置后置節點 12 p.before = p.after = null; 13 14 if (b == null) 15 //前置節點為空,說明為頭節點,更新頭節點為后置節點 16 head = a; 17 else 18 //前置節點不為空,設置前置節點的后置節點為刪除節點的后置節點 19 b.after = a; 20 if (a == null) 21 //后置節點為空,說明為尾部節點,更新尾部節點為其前置節點 22 tail = b; 23 else 24 //后置節點不為空,更新后置節點的前置節點 25 a.before = b; 26 }