自己动手实现LRU、FIFO缓存淘汰算法, LinkedHashMap的妙用
**LRU(Least recently used,最近最少使用)**算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
FIFO(First Input First Output,先进先出),即先进先出队列。在超市购物之后会提着我们满满的购物车来到收银台排在结账队伍的最后,眼睁睁地看着前面的客户一个个离开。这就是一种先进先出机制,先排队的客户先行结账离开。
为了实现LRU、FIFO算法,现在我们的LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序。于是LRU、FIFO缓存工具类的代码如下,该类的构造函数cacheSize表示缓存大小、accessOrder代表是否按照访问顺序存储(该参数值为true的时候,实现的是LRU算法;值为false时,实现的是FIFO算法)。
缓存工具类
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* LRU缓存实现
* @author Java架构师养成记
* @param <K>
* @param <V>
* */
public class CacheTools<K,V> {
private final int initCacheSize;
private final float DEFAULT_LOAD_FACTOR = 0.75f;
LinkedHashMap<K, V> cacheMap;
/**
* 缓存工具类构造方法
* @param cacheSize 缓存大小
* @param accessOrder 是否翻找访问顺序存放(false:按照插入顺序存放)
* */
public CacheTools(int cacheSize, boolean accessOrder) {
initCacheSize = cacheSize;
int capacity = (int)Math.ceil(initCacheSize / DEFAULT_LOAD_FACTOR) + 1;
cacheMap = new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTOR, accessOrder) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > initCacheSize;
}
};
}
public synchronized void put(K key, V value) {
cacheMap.put(key, value);
}
public synchronized V get(K key) {
return cacheMap.get(key);
}
public synchronized void remove(K key) {
cacheMap.remove(key);
}
public synchronized Set<Map.Entry<K, V>> getAll() {
return cacheMap.entrySet();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<K, V> entry : cacheMap.entrySet()) {
stringBuilder.append(String.format("%s:%s", entry.getKey(), entry.getValue()));
stringBuilder.append("|");
}
return stringBuilder.toString().substring(0, stringBuilder.toString().length() - 1);
}
}
LRUCache使用方法
public class LRUCache {
public static void main(String[] args) {
CacheTools<Object, Object> lruCache = new CacheTools<>(5, true);
lruCache.put("Key1", "value1");
lruCache.put("Key2", "value2");
lruCache.put("Key3", "value3");
lruCache.put("Key4", "value4");
lruCache.put("Key5", "value5");
System.out.println("未访问之前:" + lruCache);
lruCache.get("Key1");
System.out.println("访问之后:" + lruCache);
lruCache.put("Key6", "value6");
System.out.println("容量过大之后:" + lruCache);
}
}
LRU Cache效果截图:
FIFoCache使用方法:
public class FIFOCache {
public static void main(String[] args) {
CacheTools<Object, Object> fifoCache = new CacheTools<>(5, false);
fifoCache.put("Key1", "value1");
fifoCache.put("Key2", "value2");
fifoCache.put("Key3", "value3");
fifoCache.put("Key4", "value4");
fifoCache.put("Key5", "value5");
System.out.println("未访问之前\t:" + fifoCache);
fifoCache.get("Key1");
System.out.println("访问之后\t\t:" + fifoCache);
fifoCache.put("Key6", "value6");
System.out.println("容量过大之后\t:" + fifoCache);
}
}
FIFO Cache效果截图:
从上文代码可以知道,LRU和FIFO缓存的区别在于accessOrder参数的设置,当该参数被设置为true的时候,表示该Cache的思想是LRU,否则是FIFO。
欢迎大家关注我的微信公众号,不定期分享各类面试题。