LRU算法
LRU算法
简介
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的数据予以淘汰。
很多缓存中采用这种算法。
一:LinkHashMap实现LRU算法
LinkedHashMap
会对数据使用来排序,这样服务LRU算法的最少使用的思想了
public class LRUCacheDemo<k, v> extends LinkedHashMap<k, v> {
private int capacity;
public LRUCacheDemo(int capacity) {
// assesOrder 我们就清楚了当accessOrder设置为false时,会按照插入顺序进行排序,当accessOrder为true是,会按照访问顺序
super(capacity, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<k, v> eldest) {
return super.size() > capacity;
}
public static void main(String[] args) {
LRUCacheDemo lruCacheDemo = new LRUCacheDemo<>(3);
lruCacheDemo.put(1, "a");
lruCacheDemo.put(2, "b");
lruCacheDemo.put(3, "c");
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());
lruCacheDemo.put(4, "d");
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());
lruCacheDemo.get(3);
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());
lruCacheDemo.put(5, "e");
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.keySet());
}
}
我们定义一个cacheDemo类继承这个LinkedHashMap
,Cache类构造器,提供capacity 参数,作为LRU清理缓存的边界。
复写removeEldestEntry
方法,当我们缓存的容量达到设定的边界的时候,清理最少使用的缓存。
特别说明:调用父类构造的时候,有一个参数assesOrder
这个参数的作用就是,当accessOrder设置为false时,会按照插入顺序进行排序,当accessOrder为true是,会按照访问顺序,这里我们最为缓存就设置为true。
测试结果:
lruCacheDemo.keySet() = [1, 2, 3]
lruCacheDemo.keySet() = [2, 3, 4]
lruCacheDemo.keySet() = [2, 4, 3]
lruCacheDemo.keySet() = [4, 3, 5]
我们插入a,b,三个值,插入第四个的时候,第一个清理了,当我们访问3的时候,3排队到最后了,2是最少访问的。继续插入5,2就退出了。
二:手写LRU缓存
这个模式是借鉴了,AQS同步队列的,通过hash保证访问速度,链表中的头尾节点来记录最近访问,和最少访问的数据。
package com.leven.demoplus.javase.algorithm;
import java.util.HashMap;
/**
* 基于lru算法的缓存设计:最近最少使用
*/
public class LRUCacheDemo2 {
// 缓存容量
private int capacity;
// hashmap用于存数据
private HashMap<Integer,Node<Integer,Integer>> map;
// 链表数据结构维护访问次数:1:最近访问的放在头节点 2:缓存容量达到限制的时候,移除链表的尾节
private DoubleLinkList doubleLinkList;
public LRUCacheDemo2(int capacity) {
this.capacity = capacity;
map = new HashMap();
doubleLinkList = new DoubleLinkList();
}
public int get(int key){
if (!map.containsKey(key)){
return -1;
}
Node<Integer, Integer> node = map.get(key);
// 访问的数据node,移动到链表的头head
doubleLinkList.removeNode(node);
doubleLinkList.addHead(node);
return node.value;
}
public void put(Integer key, Integer value){
if (map.containsKey(key)){
Node<Integer, Integer> node = map.get(key);
node.value = value;
// 新加入数据加入头节点
doubleLinkList.removeNode(node);
doubleLinkList.addHead(node);
}else {
// 缓存空间满了
if (map.size() >= capacity){
// 容量达到限制,淘汰尾节点数据
Node lastNode = doubleLinkList.getLastNode();
doubleLinkList.removeNode(lastNode);
map.remove(lastNode.key);
}
Node<Integer, Integer> newNode = new Node<>(key, value);
map.put(key,newNode);
doubleLinkList.addHead(newNode);
}
}
class DoubleLinkList<k,v>{
Node<k,v> head;
Node<k,v> tail;
public DoubleLinkList() {
head = new Node<>();
tail = new Node<>();
head.next = tail;
tail.pre = head;
}
// 添加到头
public void addHead(Node<k,v> node){
node.next = head.next;
node.pre = head;
head.next.pre = node;
head.next = node;
}
public void removeNode(Node<k,v> node){
node.next.pre = node.pre;
node.pre.next = node.next;
node.pre = null;
node.next = null;
}
public Node<k,v> getLastNode(){
return tail.pre;
}
}
class Node<k,v>{
k key;
v value;
Node<k,v> pre;
Node<k,v> next;
public Node() {
this.pre = this.next = null;
}
public Node(k key, v value) {
this.key = key;
this.value = value;
}
}
public static void main(String[] args) {
LRUCacheDemo2 lruCacheDemo = new LRUCacheDemo2(3);
lruCacheDemo.put(1,1);
lruCacheDemo.put(2, 2);
lruCacheDemo.put(3, 3);
// 1:预期:容量达到限制,淘汰尾节点1,打印:[1, 2, 3]
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());
// 2:预期:容量达到限制,淘汰尾节点1,打印:[2, 3, 4]
lruCacheDemo.put(4, 4);
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());
// 3:预期:访问4的数据,最小访问,加入头节点,打印:[2, 3, 4]
lruCacheDemo.get(2);
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());
// 4:预期:加入5,达到限制,淘汰最久访问的缓存3(如果不执行第三步的话淘汰的应该是2),打印:[2, 4, 5]
lruCacheDemo.put(5, 5);
System.out.println("lruCacheDemo.keySet() = " + lruCacheDemo.map.keySet());
}
}
that is all!!