java集合之LinkedHashMap解析
阿新 • • 發佈:2018-12-12
LinkedHashMap 繼承自HashMap,主要結構還是HashMap,添加了雙向連結串列來保證插入順序或者訪問順序。
著名的LRUCache就是藉助了LinkedHashMap的保持訪問順序的特性實現的。
//LinkedHashMap中儲存的節點,比hashMap中的Node添加了before,after,node中還有next, //新增的兩個節點的指標,組成了雙向連結串列 static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> { LinkedHashMapEntry<K,V> before, after; LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } } //雙向連結串列的頭和尾 /** * The head (eldest) of the doubly linked list. */ transient LinkedHashMapEntry<K,V> head; /** * The tail (youngest) of the doubly linked list. */ transient LinkedHashMapEntry<K,V> tail; //true時雙向連結串列訪問順序,false時插入順序 /** * The iteration ordering method for this linked hash map: <tt>true</tt> * for access-order, <tt>false</tt> for insertion-order. * * @serial */ final boolean accessOrder;
newNode函式時hashMap中的函式,LinkedHashMap重寫了這個函式。
//生成一個新節點,然後連線到末尾 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMapEntry<K,V> p = new LinkedHashMapEntry<K,V>(hash, key, value, e); linkNodeLast(p); return p; } 連線到雙向連結串列尾 private void linkNodeLast(LinkedHashMapEntry<K,V> p) { LinkedHashMapEntry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; } } //treeNode,類似newNode 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); linkNodeLast(p); return p; }
這樣就實現了每次插入新節點,放到雙向連結串列尾部。
為了實現linkedHashMap的訪問順序排序,HashMap中預留了三個重要函式,在很多地方被呼叫但是都沒有實現:
// Callbacks to allow LinkedHashMap post-actions void afterNodeAccess(Node<K,V> p) { } void afterNodeInsertion(boolean evict) { } void afterNodeRemoval(Node<K,V> p) { }
LinkedHashMap中實現了這三個函式:
//操作過陣列和連結串列上的元素之後,修改雙向連結串列,刪除節點 void afterNodeRemoval(Node<K,V> e) { // unlink LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<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; } // 插入新的節點,呼叫過afterNodeAccess之後最終會查詢雙向連結串列刪除相同的key的節點 void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMapEntry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } } //accessOrder== true ,訪問的不是最後一個節點,保證最後一個節點時最近使用的 void afterNodeAccess(Node<K,V> e) { LinkedHashMapEntry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMapEntry<K,V> p = (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) 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; ++modCount; } }