概念
LRU(least recently used)就是将最近不被访问的数据给淘汰掉,LRU基于一种假设:认为最近使用过的数据将来被使用的概率也大,最近没有被访问的数据将来被使用的概率比较低。
原理
LRU一般通过链表形式来存放缓存数据,新插入或被访问的数据放在链表头部,超过一定阈值后,自动淘汰链表尾部的数据。下图很形象的说明了LRU缓存淘汰过程。(图片来自网络)
步骤:
1、新插入A, 将A放置在队列头部
2、新插入B, 将B放置在队列头部, A自动推举次席。
3、新插入C, 将C放置在队列头部, B自动推举次席。
4、新插入D, 将D放置在队列头部, C自动推举次席。
5、新插入E, 将E放置在队列头部, D自动推举次席。
6、新插入E, 将E放置在队列头部, 这时队列长度大于阈值,自动将尾部数据A给淘汰掉
7、访问数据C,然后将C重新放置在队列头部。
8、新插入E, 将E放置在队列头部, 这时队列长度大于阈值,自动淘汰尾部数据B
编码实现LRU策略缓存
/**
* 2020/4/11.
*
* 使用链表+hashmap来实现, 这里没有考虑并发情况, 所以在代码中没有使用锁
*/
public class LRUCache {
class CacheNode {
public CacheNode before;
public Object key;
public Object value;
public CacheNode after;
public CacheNode() {
}
}
private HashMap caches;
private int maxCapacity;
private int currentCacheSize;
/**
* 头结点, 头结点不参与淘汰,只是作为标识链表中的第一个节点
*/
private CacheNode head;
/**
* 尾节点, 尾节点不参与淘汰, 只是作为标识链表中最后一个节点
*/
private CacheNode tail;
public LRUCache(int maxCapacity) {
this.maxCapacity = maxCapacity;
caches = new HashMap<>(maxCapacity);
head = tail = new CacheNode();
head.after = tail; //对head 的after节点赋值, 防止后续操作出现空指针异常
tail.before = head; // 对tail的before节点赋值, 防止后续操作出现空指针异常
}
public void put(K k, V v) {
CacheNode node = caches.get(k);
if (node == null) {
if (currentCacheSize >= maxCapacity) {
caches.remove(tail.before.key); //淘汰尾部的元素
removeLast();
}
node = new CacheNode();
node.key = k;
currentCacheSize ++;
}
node.value = v;
moveToFirst(node); // LRU策略, 新插入的元素放置到队列头部
caches.put(k, node);
}
public void moveToFirst(CacheNode node) {
CacheNode n = head.after;
head.after = node;
node.before = head;
n.before = node;
node.after = n;
}
public void removeLast() {
CacheNode n = tail.before.before;
n.after = tail;
tail.before = n;
}
public Object get(K k) {
CacheNode node = caches.get(k);
if (node == null) {
return null;
}
Object v = node.value;
moveToFirst(node);
return v;
}
public Object remove(K k) {
CacheNode node = caches.get(k);
if (node == null) {
return null;
}
CacheNode pre = node.before;
CacheNode next = node.after;
pre.after = next;
next.before = pre;
caches.remove(k);
currentCacheSize --;
return node.value;
}
}