这是我参与更文挑战的第1天,活动详情查看: 更文挑战
LRU原理
LRU:(least recently used)最近最少使用算法,它是一种缓存逐出策略 cache eviction policies,使用场景是当内存不足时,需要淘汰最近最少使用的数据。 LRU算法是假设最近最少使用的那些数据,将来被使用的概率也不大,当内存有限的情况下,就可以把这些不常用的信息淘汰掉。比如有爆款商品时,大家都在搜索这个信息,那刚搜素的信息接下来被搜索的概率也大,就比不常用的一些商品被搜索的概率大,所以需要把很久没有用过的数据踢出去,也就是 Least Recently Used 的信息被踢出去。就是保留热点数据(最近最多使用的数据)。利用LRU我们可以解决很多实际开发中的问题,并且很符合业务场景。算法根据数据的历史访问记录来进行淘汰数据,它的主要衡量指标是使用的时间,附加指标是使用的次数。
算法分析与实现:
采用HashMap + Doubly Linked List实现。 Cache对象,需要规定缓存的容量,在初始化时,设置容量大小,然后实例化双向链表的head,tail,链表是双路的,我们定义好头节点和尾节点,然后利用先进先出(FIFO),最近被放入的数据会最早被获取 HahsMap用于快速查找到结点所在位置,然后将使用到的结点放在对头,这样最近最少使用的结点自然就落入到队尾。
public class LRUCache {
//双向链表内
public static class Node {
int key;
int val;
Node next;
Node prev;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
//Node记录表
Map<Integer, Node> map = new HashMap<>();
//双向链表头部
private Node head;
//双向链表尾部
private Node tail;
//容量
private int cap;
public LRUCache(int capacity) {
cap = capacity;
}
public int get(int key) {
Node node = map.get(key);
//如果Node不在表中,代表缓存中并没有
if(node == null) {
return -1;
} else {
//如果存在,则需要移动Node节点到表头
int res = node.val;
remove(node);
appendHead(node);
return res;
}
}
public void put(int key, int value) {
Node node = map.get(key);
//如果存在,则需要移动Node节点到表头
if(node != null) {
node.val = value;
remove(node);
appendHead(node);
} else {
//如果Node不在表中,代表缓存中并没有
node = new Node(key, value);
if(map.size() < cap) {
appendHead(node);
map.put(key, node);
} else {
// 踢走老的
map.remove(tail.key);
remove(tail);
appendHead(node);
map.put(key, node);
}
}
}
private void appendHead(Node node) {
if(head == null) {
head = tail = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
}
private void remove(Node node) {
if(head == tail) {
head = tail = null;
} else {
if(head == node) {
head = head.next;
node.next = null;
} else if (tail == node) {
tail = tail.prev;
tail.next = null;
node.prev = null;
} else {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = null;
node.next = null;
}
}
}
}