简介
LRU(Least Recently Used)直译为“最近最少使用”。其实很多老外发明的词直译过来对于我们来说并不是特别好理解,甚至有些词并不在国人的思维模式之内,比如快速排序中的Pivot,模拟信号中的Analog 等等。笔者认为最好的理解方式就是看他诞生的原因,看这个概念的出现如何一步一步演变为现在的样子。假如说你自己对某个问题想到了一个解决办法,于是你按照语义给他起了个名字,假如你直接将这个词儿说给别人,不知道这个词儿来历的人大概很难理解。所以为了力求方便理解,下面我们先来看看LRU是什么,主要是为了解决什么问题。
其实LRU这个概念映射到现实生活中非常好理解,就好比说小明的衣柜中有很多衣服,假设他的衣服都只能放在这个柜子里,小明每过一阵子小明就会买新衣服,不久小明的衣柜就放满了衣服。这个小明想了个办法,按照衣服上次穿的时间排序,丢掉最长时间没有穿过那个。这就是LRU策略。
映射到计算机概念中,上述例子中小明的衣柜就是内存,而小明的衣服就是缓存数据。我们的内存是有限的。所以当缓存数据在内存越来越多,以至于无法存放即将到来的新缓存数据时,就必须扔掉最不常用的缓存数据。所以对于LRU的抽象总结如下:
- 缓存的容量是有限的
- 当缓存容量不足以存放需要缓存的新数据时,必须丢掉最不常用的缓存数据
实现
class LRUNode {
String key;
Object value;
LRUNode prev;
LRUNode next;
public LRUNode(String key, Object value) {
this.key = key;
this.value = value;
}
}
public class LRUCache {
private HashMap<String, LRUNode> map;
private int capacity;
private LRUNode head;
private LRUNode tail;
public void set(String key, Object value) {
LRUNode node = map.get(key);
if (node != null) {
node = map.get(key);
node.value = value;
remove(node, false);
} else {
node = new LRUNode(key, value);
if (map.size() >= capacity) {
// 每次容量不足时先删除最久未使用的元素
remove(tail, true);
}
map.put(key, node);
}
// 将刚添加的元素设置为head
setHead(node);
}
public Object get(String key) {
LRUNode node = map.get(key);
if (node != null) {
// 将刚操作的元素放到head
remove(node, false);
setHead(node);
return node.value;
}
return null;
}
private void setHead(LRUNode node) {
// 先从链表中删除该元素
if (head != null) {
node.next = head;
head.prev = node;
}
head = node;
if (tail == null) {
tail = node;
}
}
// 从链表中删除此Node,此时要注意该Node是head或者是tail的情形
private void remove(LRUNode node, boolean flag) {
if (node.prev != null) {
node.prev.next = node.next;
} else {
head = node.next;
}
if (node.next != null) {
node.next.prev = node.prev;
} else {
tail = node.prev;
}
node.next = null;
node.prev = null;
if (flag) {
map.remove(node.key);
}
}
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new HashMap<String, LRUNode>();
}
}