LinkedHashMap實現本地快取
【--親測有效--】
在 Java 中使用本地快取最簡單的方式就是使 HashMap 或者 ConcurrentHashMap,對於只讀場景,兩者都可以使用,對於快取更新的場景,可以使用 ConcurrentHashMap 來保證資料的一致性,二者的使用方式非常簡單,這裡不再贅述。
+
另外,在 Java 中基於 LinkedHashMap 類,提供了一個自動清理最老元素的功能,,基於這個特質,可以將改造成一個LRU(Least Recently Used ,表示最近最少使用)快取使用。
將 LinkedHashMap 改造成快取,需要重寫 LinkedHashMap 中 removeEldestEntry(Map.Entry<K,V> eldest),這個方法,改方法是 protected 方法,不能直接呼叫,只能繼承重寫。當插入資料時(呼叫 put 或者 putAll 時)會呼叫這個方法用於判斷是否移除最老元素,返回 true 表示刪除,否則不刪除,Java 原始碼中,該方法直接返回 false,如下圖所示,看來是專門留給開發者擴充套件額。
案例程式碼如下:
package com.github.coderxing.book.code.chapter4; public class LruCache<K, V> extends LinkedHashMap<K, V> { private static final long serialVersionUID = 4504158311663914052L; private int maxCacheSize; public LruCache(int maxCacheSize) { // 第三個引數為 accessOrder,預設為false。表示按照按照訪問順序排列元素,最近訪問的元素會排雷在隊末尾 super(maxCacheSize, 0.75f, true); this.maxCacheSize = maxCacheSize; } @Override protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { // 當達到預設快取上限時刪除最老元素 return this.size() >= maxCacheSize + 1; } }
測試程式碼如下:
LruCache<String, String> cache = new LruCache<String, String>(3); cache.put("k1", "v1"); System.out.println("test1:"+cache); cache.put("k2", "v2"); System.out.println("test2:"+cache); cache.put("k3", "v3"); System.out.println("test3:"+cache); cache.put("k4", "v4"); System.out.println("test4:"+cache); //因為我們在後再物件時,accessOrder設定為true,訪問一次 k2,k2對應的元素就會排在隊尾部,被看做最新元素 cache.get("k2"); System.out.println("test5:"+cache); Map<String,String> multiKV = new HashMap<String,String>(); multiKV.put("k5", "k5"); multiKV.put("k6", "k6"); cache.putAll(multiKV); System.out.println("test5:"+cache);
輸出內容為:
test1:{k1=v1}
test2:{k1=v1, k2=v2}
test3:{k1=v1, k2=v2, k3=v3}
test4:{k2=v2, k3=v3, k4=v4}
test5:{k3=v3, k4=v4, k2=v2}
test5:{k2=v2, k5=k5, k6=k6}
從例子中可見,通過簡單的方式就可以快速實現一個LRU快取類,但 LinkedHashMap 不是執行緒安全額,在面對高併發的情況下還需要進一步封裝,比如通過 synchronized 封裝代理方法,如:
public V putCache(K key, V value) {
synchronized(this){
return this.put(key, value);
}
}
或者 Collections.synchronizedMap 進行封裝,例如:
```java
Map<String, String> cache = Collections.synchronizedMap(new LruCache<String, String>(3));
```
使用 LinkedHashMap 可以簡單快速實現一個快取框架,要想獲得更好的效能,和更強大的功能,可以參考本週後面兩節介紹的 Ehcache 和 Google Guava Cache 。