基于LinkedList实现LRU算法,但是又要克服LinkedList遍历查找的缺陷,所以引入了map来提高查询的效率。重点体会这种适当冗余的思想,会带来性能上的飞跃。
processon图如下
demo如下
/**
* @description: 双向链表O(1) ,为了改善查询O(n),引入hashmap
* 在双向链表中特意增加两个哨兵节点,不用来存储任何数据,使用哨兵节点,增加和删除节点的时候就不用考虑边界情况,降低代码复杂度
* @author: Ding Yawu
* @create: 2021/12/26 20:09
*/
public class LRUCache {
private Node head;
private Node tail;
private int size;
private int capacity;
private Map<Integer, Node> cache;
public LRUCache(int capacity) {
this.capacity = capacity;
size = 0;
cache = new HashMap<>(capacity + 2);
initLikedList();
}
/**
* 把节点加入到头结点,如果容量已满,删除掉尾结点
*
* @param key 关键
* @param value 价值
*/
public void put(int key, int value) {
Node node = cache.get(key);
if (Objects.nonNull(node)) {
//覆盖老值并且移动到头结点
node.value = value;
moveToHead(node);
return;
}
//不存在,先加进去,再移除尾结点
if (Objects.equals(size, capacity)) {
//此时容量已满,删除尾结点
Node lastNode = tail.pre;
deleteNode(lastNode);
cache.remove(lastNode.key);
size--;
}
Node newNode = new Node(key, value);
addNode(newNode);
cache.put(key, newNode);
size++;
}
public int get(int key) {
Node node = cache.get(key);
if (Objects.isNull(node)) {
return -1;
}
moveToHead(node);
return node.value;
}
private void moveToHead(Node node) {
deleteNode(node);
addNode(node);
}
private void deleteNode(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void addNode(Node node) {
head.next.pre = node;
node.next = head.next;
node.pre = head;
head.next = node;
}
//初始化链表
private void initLikedList() {
head = new Node();
tail = new Node();
head.next = tail;
tail.pre = head;
}
public static class Node {
//前驱节点、后驱节点、查询缓存的key、缓存的值
public Node pre;
public Node next;
public int key;
public int value;
public Node() {
}
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
public static void main(String[] args) {
LRUCache lruCache = new LRUCache(2);
lruCache.put(1, 1);
lruCache.put(2, 2);
System.out.println(lruCache.get(1));
lruCache.put(3, 3);
System.out.println(lruCache.get(3));
System.out.println(lruCache.get(2));
}
}