Guava---快取之LRU演算法
阿新 • • 發佈:2018-12-21
簡介
LRU全稱是Least Recently Used,即最近最久未使用的意思。
LRU演算法的設計原則是:如果一個數據在最近一段時間沒有被訪問到,那麼在將來它被訪問的可能性也很小。也就是說,當限定的空間已存滿資料時,應當把最久沒有被訪問到的資料淘汰。
實現LRU
1.用一個數組來儲存資料,給每一個數據項標記一個訪問時間戳,每次插入新資料項的時候,先把陣列中存在的資料項的時間戳自增,並將新資料項的時間戳置為0並插入到陣列中。每次訪問陣列中的資料項的時候,將被訪問的資料項的時間戳置為0。當陣列空間已滿時,將時間戳最大的資料項淘汰。
2.利用一個連結串列來實現,每次新插入資料的時候將新資料插到連結串列的頭部;每次快取命中(即資料被訪問),則將資料移到連結串列頭部;那麼當連結串列滿的時候,就將連結串列尾部的資料丟棄。
3.利用連結串列和hashmap。當需要插入新的資料項的時候,如果新資料項在連結串列中存在(一般稱為命中),則把該節點移到連結串列頭部,如果不存在,則新建一個節點,放到連結串列頭部,若快取滿了,則把連結串列最後一個節點刪除即可。在訪問資料的時候,如果資料項在連結串列中存在,則把該節點移到連結串列頭部,否則返回-1。這樣一來在連結串列尾部的節點就是最近最久未訪問的資料項。
對於第一種方法,需要不停地維護資料項的訪問時間戳,另外,在插入資料、刪除資料以及訪問資料時,時間複雜度都是O(n)。對於第二種方法,連結串列在定位資料的時候時間複雜度為O(n)。所以在一般使用第三種方式來是實現LRU演算法。
LinkedHashMap中LRU演算法實現
/** * @Author: Kingcym * @Description: 非執行緒安全 * @Date: 2018/11/11 19:09 */ public class LinkedHashLRUcache<k, v> { /** * LinkedHashMap(自身實現了LRU演算法) * 1.有序 * 2.每次訪問一個元素,都會提到最後面去 */ private static class InternalLRUcache<k, v> extends LinkedHashMap<k, v> { private final int limit; private InternalLRUcache(int limit) { super(16, 0.75f, true); this.limit = limit; } //是否刪除最老的資料 @Override protected boolean removeEldestEntry(Map.Entry<k, v> eldest) { return size() > limit; } } private final int limit; private final InternalLRUcache<k, v> internalLRUcache; public LinkedHashLRUcache(int limit) { Assert.state(limit > 0, "limit必須大於0"); this.limit = limit; this.internalLRUcache = new InternalLRUcache(limit); } public void put(k key, v value) { this.internalLRUcache.put(key, value); } public v get(k key) { return this.internalLRUcache.get(key); } public void remove(k key) { this.internalLRUcache.remove(key); } public int size() { return this.internalLRUcache.size(); } public void clear() { this.internalLRUcache.clear(); } public String toString() { return internalLRUcache.toString(); } }
當存在熱點資料時,LRU的效率很好,但偶發性的、週期性的批量操作會導致LRU命中率急劇下降,快取汙染情況比較嚴重。