若是須要使用的Map中的key無序,選擇HashMap
;若是要求key有序,則選擇TreeMap
。 可是選擇TreeMap就會有性能問題,由於TreeMap的get操做的時間複雜度是O(log(n))
的,相比於HashMap的O(1)
仍是差很多的,LinkedHashMap
的出現就是爲了平衡這些因素,使得 可以以O(1)時間複雜度增長查找元素,又可以保證key的有序性 此外,LinkedHashMap提供了兩種key的順序:java
LRU(Least Recently Used)
緩存類聲明node
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
繼承自HashMap,此處實現Map接口,用來代表該類是Map系的類。緩存
LinkedHashMap
中採用的這種環型雙向鏈表.final boolean accessOrder;//true:key的順序爲訪問順序;false:key的順序爲插入式順序。默認爲插入順序 transient LinkedHashMap.Entry<K,V> head;//雙向鏈表頭結點 transient LinkedHashMap.Entry<K,V> tail;//雙向鏈表尾結點
public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); //默認爲false,也就是插入順序 accessOrder = false; } public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } public LinkedHashMap() { super(); accessOrder = false; } public LinkedHashMap(Map<? extends K, ? extends V> m) { super(); accessOrder = false; putMapEntries(m, false); } public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
主要基於HashMap的節點數據結構實現。數據結構
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); } }
雙向鏈表實現的LinkedHshMap,因此每一個節點須在HashMap的基礎上添加指向前繼節點與後繼節點指針:before,after。函數
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; } }
在最後添加一個節點,須要取LinkedHashMap的末尾節點,性能
void afterNodeRemoval(Node<K,V> e) { // unlink 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; }
刪除一個節點時,須要把this
afterNodeRemoval指針
void afterNodeRemoval(Node<K,V> e) { // unlink 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; }
移除節點後調用的,就是將節點從雙向鏈表中刪除code
afterNodeInsertion排序
void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; // 若是定義了溢出規則,則執行相應的溢出 if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } }
若是用戶定義了removeEldestEntry
的規則,那麼即可以執行相應的移除操做。
afterNodeAccess
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; // 若是定義了accessOrder,那麼就保證最近訪問節點放到最後 if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; //e的前驅還有節點,e不是頭結點,不然e是頭結點。 if (b == null) head = a;//把頭結點後移一個 else b.after = a;//把中間節點移除 //e是否是tail節點,是tail節點,則使last爲e的前驅 if (a != null) a.before = b; else last = b; //鏈表中只有節點e if (last == null) head = p; else {//將p移到最後 p.before = last; last.after = p; } tail = p; ++modCount; } }
進行put以後,對節點的訪問了,那麼這個時候就會更新鏈表,把最近訪問的放到最後,保證鏈表的key按照訪問有序。
put put
函數在LinkedHashMap
中未從新實現,只是實現了afterNodeAccess
和afterNodeInsertion
兩個回調函數。
get
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; }
get
函數從新實現並加入了afterNodeAccess
來保證訪問順序
LinkedHashMap
中覆蓋HashMap
中newNode
方法,使得每次put數據時,新建的節點都是LinkedHashMap.Entry<K,v>
類型的,比普通的HsahMap.Entry
多一個前驅結點和一個後繼節點,使用前驅和後繼保證插入有序。HashMap
的afterNodeAccess
方法,使得每次訪問後,都改變鏈表順序。使得原鏈表按訪問排序。將最新一次訪問的節點放到鏈表的最後。Thanks for reading! want more