LRU算法简介
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久没有使用的数据予以淘汰。
核心思想:哈希表+双向链表
实现一:利用LinkedHashMap
import java.util.LinkedHashMap;
public class LRUCache<K,V> extends LinkedHashMap<K, V>{
private int capacity; //缓存坑位
public LRUCache(int capacity) {
/**
* @param Capacity the initial capacity
* @param loadFactor the load factor 装载因子,底层代码是0.75,这里写死,也写成0.75
* @param accessOrder the ordering mode - <tt>true</tt> for
* access-order, <tt>false</tt> for insertion-order 访问顺序
*/
super(capacity,(float) 0.75,true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
// TODO Auto-generated method stub
return super.size()>capacity;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
LRUCache lru = new LRUCache<>(3);
lru.put(1, "a");
lru.put(2, "b");
lru.put(3, "c");
System.out.println(lru.keySet());
lru.put(4, "d");
System.out.println(lru.keySet());
lru.put(3, "c");
System.out.println(lru.keySet());
lru.put(3, "c");
System.out.println(lru.keySet());
lru.put(3, "c");
System.out.println(lru.keySet());
lru.put(5, "x");
System.out.println(lru.keySet());
}
}
输出(这是在accessOrder也就是访问顺序为true的情况下的结果):
输出(这是在accessOrder也就是访问顺序为false的情况下的结果):
实现二:手写LRU,不依赖JDK
import java.util.HashMap;
import java.util.Map;
public class LRUCache{
//1、构造一个Node节点作为数据载体
class Node<K,V>{
K key;
V value;
Node<K,V> prev;
Node<K,V> next;
public Node() {
this.prev=this.next=null;
}
public Node(K key,V value) {
this.key=key;
this.value=value;
this.prev=this.next=null;
}
}
//2、构建一个双向链表,里面放的就是我们的Node
class DoubleLinkedList<K,V>{
Node<K,V> head;
Node<K,V> tail;
//2.1 构造方法
public DoubleLinkedList() {
head = new Node<>();
tail = new Node<>();
head.next = tail;
tail.prev = head;
}
//2.2 添加到头
public void addHead(Node<K,V> node) {
node.next=head.next;
node.prev=head;
head.next.prev=node;
head.next=node;
}
//2.3 删除节点
public void removeNode(Node<K,V> node) {
node.next.prev = node.prev;
node.prev.next = node.next;
node.prev=null;
node.next=null;
}
//2.4 获得最后一个节点
public Node getLast() {
return tail.prev;
}
}
private int cacheSize;
Map<Integer,Node<Integer,Integer>> map;
DoubleLinkedList<Integer, Integer> doubleLinkedList;
public LRUCache(int cacheSize) {
this.cacheSize = cacheSize;
map = new HashMap<Integer, LRUCache.Node<Integer,Integer>>();
doubleLinkedList = new DoubleLinkedList<>();
}
public int get(int key) {
if(!map.containsKey(key)) {
return -1;
}
//表示要访问节点
Node<Integer,Integer> node = map.get(key);
//将要访问的节点删除
doubleLinkedList.removeNode(node);
//然后再将该节点添加到头部
doubleLinkedList.addHead(node);
return node.value;
}
public void put(int key,int value) {
if(map.containsKey(key)) {
//如果当前key已经存在map中,那么value的值要被覆盖掉
Node<Integer,Integer> node = map.get(key);
node.value = value;
map.put(key, node);
//同时删除添加进来的节点,然后将该节点放在最前面,表示最近访问过的节点
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
} else {
if(map.size()==cacheSize) {
//如果这个时候map的大小已经达到了定义的cacheSize,那么将最后一个节点删除
Node<Integer,Integer> lastNode = doubleLinkedList.getLast();
map.remove(lastNode.key);
doubleLinkedList.removeNode(lastNode);
}
//新增一个node
Node<Integer,Integer> node = new Node<>(key,value);
map.put(key, node);
doubleLinkedList.addHead(node);
}
//遍历一下doubleLinkedList 关系里面的key值顺序
Node temp = doubleLinkedList.head.next;
while(temp!=doubleLinkedList.tail) {
System.out.print(temp.key+" ");
temp=temp.next;
}
System.out.println();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
LRUCache lru = new LRUCache(3);
lru.put(1, 1);
lru.put(2, 2);
lru.put(3, 3);
lru.put(4, 4);
lru.put(3, 3);
lru.put(3, 3);
lru.put(3, 3);
lru.put(5, 5);
}
}
输出: