緩存淘汰算法之LRU實現
阿新 • • 發佈:2017-09-01
性能 current 實現簡單 fault 數量 進行 strong stat xtend
Java中最簡單的LRU算法實現,就是利用 LinkedHashMap,覆寫其中的removeEldestEntry(Map.Entry)方法即可
如果你去看LinkedHashMap的源碼可知,LRU算法是通過雙向鏈表來實現,當某個位置被命中,通過調整鏈表的指向將該位置調整到頭位置,新加入的內容直接放在鏈表頭, 如此一來,最近被命中的內容就向鏈表頭移動,需要替換時,鏈表最後的位置就是最近最少使用的位置。import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; importjava.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * LinkedHashMap實現簡單的緩存, 必須實現removeEldestEntry方法,具體參見JDK文檔 * @author * 2017年9月1日 */ public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> { private final intmaxCapacity; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private final Lock lock = new ReentrantLock(); public LRULinkedHashMap(int maxCapacity) { super(maxCapacity, DEFAULT_LOAD_FACTOR, true); this.maxCapacity = maxCapacity; } @Overrideprotected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { return size() > maxCapacity; } @Override public boolean containsKey(Object key) { try { lock.lock(); return super.containsKey(key); } finally { lock.unlock(); } } @Override public V get(Object key) { try { lock.lock(); return super.get(key); } finally { lock.unlock(); } } @Override public V put(K key, V value) { try { lock.lock(); return super.put(key, value); } finally { lock.unlock(); } } public int size() { try { lock.lock(); return super.size(); } finally { lock.unlock(); } } public void clear() { try { lock.lock(); super.clear(); } finally { lock.unlock(); } } public Collection<Map.Entry<K, V>> getAll() { try { lock.lock(); return new ArrayList<Map.Entry<K, V>>(super.entrySet()); } finally { lock.unlock(); } } }
基於雙鏈表的LRU實現
傳統意義的LRU算法是為每一個Cache對象設置一個計數器,每次Cache命中則給計數器+1,而Cache用完,需要淘汰舊內容,放置新內容時,就查看所有的計數器,並將最少使用的內容替換掉。
它的弊端很明顯,如果Cache的數量少,問題不會很大, 但是如果Cache的空間過大,達到10W或者100W以上,一旦需要淘汰,則需要遍歷所有計算器,其性能與資源消耗是巨大的。效率也就非常的慢了。
它的原理: 將Cache的所有位置都用雙連表連接起來,當一個位置被命中之後,就將通過調整鏈表的指向,將該位置調整到鏈表頭的位置,新加入的Cache直接加到鏈表頭中。
這樣,在多次進行Cache操作後,最近被命中的,就會被向鏈表頭方向移動,而沒有命中的,而想鏈表後面移動,鏈表尾則表示最近最少使用的Cache。
當需要替換內容時候,鏈表的最後位置就是最少被命中的位置,我們只需要淘汰鏈表最後的部分即可。
import java.util.Hashtable; public class LRUCache { class CacheNode { CacheNode prev;//前一節點 CacheNode next;//後一節點 Object value;//值 Object key;//鍵 CacheNode() { } } private int cacheSize; private Hashtable nodes;//緩存容器 private int currentSize; private CacheNode first;//鏈表頭 private CacheNode last;//鏈表尾 public LRUCache(int i) { currentSize = 0; cacheSize = i; nodes = new Hashtable(i);//緩存容器 } /** * 獲取緩存中對象 * @param key * @return */ public Object get(Object key) { CacheNode node = (CacheNode) nodes.get(key); if (node != null) { moveToHead(node); return node.value; } else { return null; } } /** * 添加緩存 * @param key * @param value */ public void put(Object key, Object value) { CacheNode node = (CacheNode) nodes.get(key); if (node == null) { //緩存容器是否已經超過大小. if (currentSize >= cacheSize) { if (last != null)//將最少使用的刪除 nodes.remove(last.key); removeLast(); } else { currentSize++; } node = new CacheNode(); } node.value = value; node.key = key; //將最新使用的節點放到鏈表頭,表示最新使用的. moveToHead(node); nodes.put(key, node); } /** * 將緩存刪除 * @param key * @return */ public Object remove(Object key) { CacheNode node = (CacheNode) nodes.get(key); if (node != null) { if (node.prev != null) { node.prev.next = node.next; } if (node.next != null) { node.next.prev = node.prev; } if (last == node) last = node.prev; if (first == node) first = node.next; } return node; } public void clear() { first = null; last = null; } /** * 刪除鏈表尾部節點 * 表示 刪除最少使用的緩存對象 */ private void removeLast() { //鏈表尾不為空,則將鏈表尾指向null. 刪除連表尾(刪除最少使用的緩存對象) if (last != null) { if (last.prev != null) last.prev.next = null; else first = null; last = last.prev; } } /** * 移動到鏈表頭,表示這個節點是最新使用過的 * @param node */ private void moveToHead(CacheNode node) { if (node == first) return; if (node.prev != null) node.prev.next = node.next; if (node.next != null) node.next.prev = node.prev; if (last == node) last = node.prev; if (first != null) { node.next = first; first.prev = node; } first = node; node.prev = null; if (last == null) last = first; } }
緩存淘汰算法之LRU實現