GuavaCache學習筆記一:自定義LRU演算法的快取實現
阿新 • • 發佈:2018-12-09
前言
今天在看GuavaCache快取相關的原始碼,這裡想到先自己手動實現一個LRU演算法。於是乎便想到LinkedHashMap和LinkedList+HashMap, 這裡僅僅是作為簡單的複習一下。
LRU
LRU(Least recently used,最近最少使用)演算法根據資料的歷史訪問記錄來進行淘汰資料,其核心思想是“如果資料最近被訪問過,那麼將來被訪問的機率也更高”。
程式碼實現原理
LinkedList + HashMap: LinkedList其實是一個雙向連結串列,我們可以通過get和put來設定最近請求key的位置,然後hashMap去儲存資料
LinkedHashMap:LinkedHashMap是繼承自HashMap,只不過Map中的Node節點改為了雙向節點,雙向節點可以維護新增的順序,在LinkedHashMap的建構函式中有一個accessOrder, 當設定為true後,put和get會自動維護最近請求的位置到last。
LinkedList+HashMap程式碼實現
LRUCache介面:
/** * @Description: * @Author: wangmeng * @Date: 2018/12/8-10:49 */ public class LinkedListLRUTest { public static void main(String[] args) { LRUCache<String, String> cache = new LinkedListLRUCache<>(3); cache.put("1", "1"); cache.put("2", "2"); cache.put("3", "3"); System.out.println(cache); cache.put("4", "4"); System.out.println(cache); System.out.println(cache.get("2")); System.out.println(cache); } }
LinkedList實現:
/** * @Description:使用LinkedList+HashMap來實現LRU演算法 * @Author: wangmeng * @Date: 2018/12/8-10:41 */ public class LinkedListLRUCache<K, V> implements LRUCache<K, V> { private final int limit; private final LinkedList<K> keys = new LinkedList<>(); private final Map<K, V> cache = Maps.newHashMap(); public LinkedListLRUCache(int limit) { this.limit = limit; } @Override public void put(K key, V value) { Preconditions.checkNotNull(key); Preconditions.checkNotNull(value); if (keys.size() >= limit) { K oldesKey = keys.removeFirst(); cache.remove(oldesKey); } keys.addLast(key); cache.put(key, value); } @Override public V get(K key) { boolean exist = keys.remove(key); if (!exist) { return null; } keys.addLast(key); return cache.get(key); } @Override public void remove(K key) { boolean exist = keys.remove(key); if (exist) { keys.remove(key); cache.remove(key); } } @Override public int size() { return keys.size(); } @Override public void clear() { keys.clear(); cache.clear(); } @Override public int limit() { return this.limit; } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (K key : keys) { builder.append(key).append("=").append(cache.get(key)).append(";"); } return builder.toString(); } }
LinkedList測試類:
/**
* @Description:
* @Author: wangmeng
* @Date: 2018/12/8-10:49
*/
public class LinkedListLRUTest {
public static void main(String[] args) {
LRUCache<String, String> cache = new LinkedListLRUCache<>(3);
cache.put("1", "1");
cache.put("2", "2");
cache.put("3", "3");
System.out.println(cache);
cache.put("4", "4");
System.out.println(cache);
System.out.println(cache.get("2"));
System.out.println(cache);
}
}
LinkedList測試類返回值:
1=1;2=2;3=3;
2=2;3=3;4=4;
2
3=3;4=4;2=2;
LinkedHashMap實現
/**
* @Description: 不是一個執行緒安全的類,這裡是使用LinkedHashMap來做LRU演算法
* @Author: wangmeng
* @Date: 2018/12/8-10:14
*/
public class LinkedHashLRUCache<K, V> implements LRUCache<K, V> {
private static class InternalLRUCache<K, V> extends LinkedHashMap<K, V> {
final private int limit;
private InternalLRUCache(int limit) {
super(16, 0.75f, true);
this.limit = limit ;
}
//實現remove元素的方法,這個是重寫了LinkedHashMap中的方法。因為在HashMap的putVal會呼叫afterNodeInsertion(), 而這個方法會判斷removeEldestEntry方法。
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > limit;
}
}
private final int limit;
//使用組合關係優於繼承,這裡只對外暴漏LRUCache中的方法
private final InternalLRUCache<K, V> internalLRUCache;
public LinkedHashLRUCache(int limit) {
Preconditions.checkArgument(limit > 0, "The limit big than zero.");
this.limit = limit;
this.internalLRUCache = new InternalLRUCache(limit);
}
@Override
public void put(K key, V value) {
this.internalLRUCache.put(key, value);
}
@Override
public V get(K key) {
return this.internalLRUCache.get(key);
}
@Override
public void remove(K key) {
this.internalLRUCache.remove(key);
}
@Override
public int size() {
return this.internalLRUCache.size();
}
@Override
public void clear() {
this.internalLRUCache.clear();
}
@Override
public int limit() {
return this.limit;
}
@Override
public String toString() {
return internalLRUCache.toString();
}
}
LinkedHashMap測試類:
/**
* @Description:
* @Author: wangmeng
* @Date: 2018/12/8-10:30
*/
public class LinkedHashLRUTest {
public static void main(String[] args) {
LRUCache<String, String> cache = new LinkedHashLRUCache<>(3);
cache.put("1", "1");
cache.put("2", "2");
cache.put("3", "3");
System.out.println(cache);
cache.put("4", "4");
System.out.println(cache);
System.out.println(cache.get("2"));
System.out.println(cache);
}
}
LinkedHashMap測試結果:
{1=1, 2=2, 3=3}
{2=2, 3=3, 4=4}
2
{3=3, 4=4, 2=2}
來自為知筆記(Wiz)