LRU缓存淘汰策略

119 阅读2分钟

LRU(Least Recently Used)是一种缓存淘汰策略,它会优先淘汰最近最少使用的数据。在Redis中,LRU算法被广泛应用于内存缓存和磁盘缓存中。在Java中,可以使用双向链表和字典来实现一个简单的LRU算法。

以下是使用Java的双向链表和字典实现LRU算法的示例代码:

import java.util.HashMap;
import java.util.Map;
​
public class LRUCache<K,V> {
    private final int capacity;//最大容量
    private final Map<K, Node> cache;//缓存
    private final Node head;//头节点
    private final Node tail;//尾节点
​
    //构造器,需要传入 最大容量
    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new HashMap<>(capacity);
        //创建一个头节点和尾节点,并且将二者连接起来
        this.head = new Node(null, null);
        this.tail = new Node(null, null);
        this.head.next = this.tail;
        this.tail.prev = this.head;
    }
​
    public void put(K key, V value) {
        Node node = cache.get(key);//尝试从缓存中获取节点
        if (node != null) {//获取到了节点
            node.value = value;//更新节点信息
            moveNodeToHead(node);//将节点移动到头部
        } else {//没有获取到节点
            node = new Node(key, value);//new新节点
            cache.put(key, node);//将节点放入缓存
            addToHead(node);//将节点放入头部
            if (cache.size() > capacity) {//如果当前节点数量超过最大容量(capacity)
                Node lastNode = removeLast();//从双向链表移除位于最后的节点
                cache.remove(lastNode.key);//从缓存(map)中移除“最后的节点”
            }
        }
    }
​
    public V get(K key) {//get方法,查缓存(map)
        Node node = cache.get(key);
        if (node != null) {
            moveNodeToHead(node);
            return node.value;
        }
        return null;
    }
​
    //将传入节点放入双向链表头部
    private void addToHead(Node node) {
        /**
         * 更改了四个地方,按顺序分别是:
         * head的next:
         * 传入node的prev,next:
         * head.next的prev:
         */
        node.prev = head;//设置传入节点的上一个节点为head
        node.next = head.next;//设置传入节点的下一个节点为 之前head的下一个节点
        head.next.prev = node;//将head之前的next节点的prev设置为传入节点
        head.next = node;//设置head的next为传入节点
    }
​
    private void moveNodeToHead(Node node) {
        removeNode(node);
        addToHead(node);
    }
​
    //移除最后一个节点
    private Node removeLast() {
        Node lastNode = tail.prev;//拿到尾节点的prev,命名为lastNode
        removeNode(lastNode);//调用移除方法
        return lastNode;//将last节点返回
    }
​
    //移除方法:logic:将传入节点的prev的next属性:
    // 传入节点的next的prev属性做一个更改,使得 上一节点(对于传入节点来说) 直接连接上 下一节点(对于传入节点来说)
    //而 当前节点 与它的上下节点没有(双向)联系,而它的上下节点产生了双向联系,逻辑上相当于将当前节点挂掉了
    private void removeNode(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }
​
    private class Node {
        private final K key;
        private V value;
        private Node prev;//它的上一个节点
        private Node next;//它的下一个节点
​
        private Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }
}

在该示例代码中,LRUCache类是一个泛型类,用于存储任意类型的键值对。它包含一个双向链表和一个字典,用于存储数据和快速查找数据。在LRUCache类的构造函数中,创建一个头结点和一个尾结点,并将它们相互连接成一个双向链表。

LRUCache类包含三个私有方法addToHead、moveNodeToHead和removeNode,用于在双向链表中添加、移动和删除结点。其中,addToHead方法将一个结点添加到双向链表的头部,moveNodeToHead方法将一个结点移动到双向链表的头部,removeNode方法删除一个结点。

在LRUCache类中,put方法用于存储数据,get方法用于获取数据。当调用put方法存储数据