LinkedHashMap 實現LRU快取
阿新 • • 發佈:2020-10-22
date: 2020-07-09 13:52:00
updated: 2020-07-21 17:40:00
LinkedHashMap 實現LRU快取
LinkedHashMap是HashMap的子類,但是內部還有一個雙向連結串列維護鍵值對的順序,每個鍵值對既位於雜湊表中,也位於雙向連結串列中。LinkedHashMap支援兩種順序插入順序 、 訪問順序
插入順序:先新增的在前面,後新增的在後面。修改操作不影響順序
訪問順序:所謂訪問指的是get/put操作,對一個鍵執行get/put操作後,其對應的鍵值對會移動到連結串列末尾,所以最末尾的是最近訪問的,最開始的是最久沒有被訪問的,這就是訪問順序。
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
LRU(Least Recently Used): 最近最少使用
public class LRUCache<K, V> extends LinkedHashMap<K, V> { private int maxEntries; public LRUCache(int maxEntries) { super(16, 0.75f, true); this.maxEntries = maxEntries; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > maxEntries; } }
在LinkedHashMap新增元素後,會呼叫removeEldestEntry防範,傳遞的引數時最久沒有被訪問的鍵值對,如果方法返回true,這個最久的鍵值對就會被刪除。LinkedHashMap中的實現總返回false,該子類重寫後即可實現對容量的控制。
LRUCache<String,Object> cache = new LRUCache<>(3); cache.put("a","abstract"); cache.put("b","basic"); cache.put("c","call"); cache.get("a"); cache.put("d","滴滴滴"); System.out.println(cache); // 輸出為:{c=call, a=abstract, d=滴滴滴}
相比HashMap,LinkedHashMap還實現了三個方法,當且僅當 accessOrder=True 時會被呼叫到
void afterNodeAccess(Node<K,V> p) { } //訪問節點之後呼叫的方法
void afterNodeInsertion(boolean evict) { } //插入節點之後呼叫的方法
void afterNodeRemoval(Node<K,V> p) { } //刪除節點之後呼叫的方法
在get()方法中呼叫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;
}
}
LinkedHashMap的put()是呼叫的父類HashMap的put()方法
這個方法是在HashMap中的put()方法中被呼叫,在LinkedHashMap中被實現,具體作用是:在插入新節點後,因為快取不夠,需要刪除最近最少使用的節點。要成功呼叫這個方法,還需要使用者覆寫 removeEldestEntry(first) 方法
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);
}
}
當LinkedHashMap成功呼叫removeNode()方法,刪除節點之後,會呼叫本方法
具體作用是:在removeNode方法中,只是刪除了HashMap中的節點,並沒有在連結串列中刪除。所以在removeNode中,回調了這個方法,將該節點從連結串列中刪除(這裡是刪除的頭結點,因為頭結點是最早進入或者最近最久未使用的)。
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;
}