本文以jdk1.8中LinkedHashMap.afterNodeAccess()方法爲切入點,分析其中難理解、有價值的源碼片斷(相似源碼查看是ctrl+鼠標左鍵的過程)。觀光線路圖:afterNodeAccess() --> afterNodeInsertion() --> removeEldestEntry() --> afterNodeRemoval() --> internalWriteEntries() ...java
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<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; } } //////////////////////////////// 涉及變量以下: /** * 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; /** * 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;
上回在HashMap.afterNodeAccess()中說道,「是爲LinkedHashMap留的後路」。現在行至於此,當觀賞一方。首先須要瞭解的是LinkedHashMap相比HashMap多了有序性,由雙向鏈表(before,after)實現。源碼出現了一些全局變量:node
accessOrder:true:按訪問順序排序(LRU),false:按插入順序排序;緩存
head、tail:存放鏈表首尾;併發
可見僅有accessOrder爲true時,且訪問節點不等於尾節點時,該方法纔有意義。經過before、after重定向,將新訪問節點連接爲鏈表尾節點。ide
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); } } protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }
細心的你也花現了吧。afterNodeInsertion()因爲removeEldestEntry()所返回的false無執行意義。也就意味着若是想要讓它有意義必須重寫removeEldestEntry()。this
如,使用LinkedHashMap實現一個簡單的LRU(Least Recently Used)Cache。那麼就應該重寫removeEldestEntry(),當超出緩存容器大小時移除最老的首節點(這裏不考慮併發問題,以下):.net
@Override public boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
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; }
afterNodeRemoval()方法相對簡單,就是在刪除後處理其對應鏈表先後關係(刨掉一截)。code
LinkedHashMap源碼閱讀整體門檻相對而言比HashMap,畢竟大多數底層put,get都由HashMap實現了。internalWriteEntries()相對來講比較突兀,若是你知道它在哪裏起着什麼樣神祕的做用請在評論裏告訴在下吧。[比心❤]blog
void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) { s.writeObject(e.key); s.writeObject(e.value); } }
可經過這篇文章理解建立一個LinkedHashMap實例過程(圖):排序
Java_LinkedHashMap工做原理 2017-05-04;
往期線路回顧:
【java源碼一帶一路系列】之HashMap.putAll()
【java源碼一帶一路系列】之HashMap.putVal()
【java源碼一帶一路系列】之HashMap.compute()