【java源碼一帶一路系列】之LinkedHashMap.afterNodeAccess()

本文以jdk1.8中LinkedHashMap.afterNodeAccess()方法爲切入點,分析其中難理解、有價值的源碼片斷(相似源碼查看是ctrl+鼠標左鍵的過程)。觀光線路圖:afterNodeAccess() --> afterNodeInsertion() --> removeEldestEntry() --> afterNodeRemoval() --> internalWriteEntries() ...java

☞ afterNodeAccess()

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

☞ 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);
    }
}

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;          
}

☞ 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;
    }

afterNodeRemoval()方法相對簡單,就是在刪除後處理其對應鏈表先後關係(刨掉一截)。code

☞ internalWriteEntries()

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()

相關文章
相關標籤/搜索