图解LRU算法
Posted Dream_it_possible!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解LRU算法相关的知识,希望对你有一定的参考价值。
一、什么是LRU算法?
LRU 全称Least Recently Used, LRU算法是一种操作系统层面上的页面置换算法,将最近最久未使用的页面进行淘汰。
二、基于双向链表+Map实现LRU算法
为什么选用双向链表做cache?
思想: 将多次访问的节点移到头节点,将少访问的节点移到尾节点。
由于双向链表的自身特性,它能够很方便地找到上一个节点和下一个节点,对节点的操作相对来说比较容易。单向的不能找到上一个节点,在操作时不能提供当前节点的上一个节点遍历。
1. 用双向链表看成cache缓存, 数据存放在链表上的每个节点上。
在使用cache时,主要有2种操作,一种是向cache里添加数据, 另一种是从cache里获取数据。
对于get操作:
1) 如果map 里不存在,那么直接返回null。
2) 如果map里存在,找到将该节点移到到双向链表的表头。
对于put操作:
1) 先判断用上述的get操作获取到node, 如果node不存在,接着通过 history.size()==capcity 判断容量是否已满,如果满了,就将尾节点从map 中移除,同时清除掉双向链表中的尾节点。
2) 如果node存在,那么就cache(双向链表) 中的对应的node移到链表头。
2. 用Map记录访问cache的历史, 只要访问了 cache就将节点放置Map里。
3. 图解移动节点和淘汰策略过程
只要画一下图,我们就会发现双向链表的优点体现出来了。
三、完整代码
package sort;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author bingbing
* @date 2021/5/7 0007 9:58
*/
public class LRUCache<V> {
private int capacity = 1024;
private static Map<String, Node> history = new ConcurrentHashMap<>();
Node head;
Node tail;
public LRUCache(int capacity) {
this();
this.capacity = capacity;
}
public LRUCache() {
head = new Node();
tail = new Node();
head.next = tail;
head.pre = null;
tail.pre = head;
tail.next = null;
}
private V get(String key) {
Node node = history.get(key);
if (null == node) {
return null;
}
// 将节点移动表头
node.pre.next = node.next;
node.next.pre = node.pre;
node.next=head.next;
head.next.pre=node;
head.next=node;
node.pre=head;
return (V) node;
}
private void put(String key, V value) {
Node node = history.get(key);
if (null == node) {
// 判断容量是否已满
if (history.size() == capacity) {
history.remove(tail.pre.key);
tail.pre = tail.next;
tail.next = null;
tail = tail.pre;
}
}
// 插入到表头
node = (Node) value;
history.put(key, node);
node.next = head.next;
head.next.pre = node;
head.next = node;
node.pre = head;
}
static class Node<k, v> {
private k key;
private v value;
Node<k, v> pre;
Node<k, v> next;
public Node(k key, v value) {
this.key = key;
this.value = value;
}
public Node() {
}
}
public static void main(String[] args) {
LRUCache<Node> cache = new LRUCache<>(4);
Node<String, Integer> node1 = new Node<String, Integer>("k1", 1);
Node<String, Integer> node2 = new Node<String, Integer>("k2", 2);
Node<String, Integer> node3 = new Node<String, Integer>("k3", 3);
Node<String, Integer> node4 = new Node<String, Integer>("k4", 4);
Node<String, Integer> node5 = new Node<String, Integer>("k5", 5);
cache.put("k1", node1);
cache.put("k2", node2);
cache.put("k3", node3);
cache.put("k4", node4);
// cache.get("k1");
cache.put("k5", node5);
Node node = cache.get("k1");
System.out.println(node);
}
}
效果演示:
1) 注释掉 cache.get("k1"), 从cache中拿k1, 查看结果
null
2) 放开 cache.get("k1"), 从cache中拿k1, 查看结果
sort.LRUCache$Node@14ae5a5
第一次的容量只有4,按照最少使用规则,那么就会淘汰k1, 因此第一次测试结果为null, 第二次测试因为重新获取了k1, k1被移到了头节点,淘汰的是k2, 因此能够拿到k1。
参考:
https://blog.csdn.net/belongtocode/article/details/102989685
以上是关于图解LRU算法的主要内容,如果未能解决你的问题,请参考以下文章