LRU缓存算法

什么是LRU算法

LRU的全名为 Least Recently Used,意思是“最近少用”,是一种常用的页面置换算法,选择最近最久未使用的数据予以淘汰。典型的Redis缓存就采用了各类变种的LRU算法。

  • 特性要求:

    • 必须要有顺序之分,用来区分最近使用和很久没有使用的数据排序。
    • 写和读操作一次完成
    • 如果容量满了要删除最不常用的数据,每次新访问还要把新的数据插入到队头(左右自己设定)
  • LRU的算法核心是哈希链表。哈希一次就能查到,满足查找快;增删用链表比较快。

用LinkedHashMap完成LRU算法

LinkedHashMap的源码都表示,这种 map 非常适合于构建LRU缓存。

class LRUCache extends LinkedHashMap {
    private int capacity;

    public LRUCache(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    public int get(int key) {
        return super.getOrDefault(key, -1);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > capacity;
    }
    
}

手写LRU(不依赖JDK)

map负责查找,构建一个虚拟的双向链表,里面安装的是一个个的Node节点,作为数据载体。

Node节点:

// 1、构造一个Node节点,作为数据载体
class Node {
    K key;
    V value;
    Node prev;
    Node 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 {

    Node head;
    Node tail;

    // 构造方法
    public DoubleLinkedList() {
        head = new Node<>();
        tail = new Node<>();
        head.next = tail;
        tail.prev = head;
    }

    // 添加到头
    public void addHead(Node node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }

    // 删除节点
    public void removeNode(Node node) {
        node.next.prev = node.prev;
        node.prev.next = node.next;
        node.prev = null;
        node.next = null;
    }

    // 获得最后一个节点
    public Node getLast() {
        return tail.prev;
    }

}

实现LRU:

public class LRUCache {
    
    private int capacity;

    Map> map;
    DoubleLinkedList doubleLinkedList;

    public LRUCache(int capacity) {
        this.cacheSize = capacity;
        map = new HashMap<>(); // 用于查找
        doubleLinkedList = new DoubleLinkedList<>();
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }

        Node node = map.get(key);

        // 先删掉原来的位置,再加入到头结点,就是最近使用的节点了
        doubleLinkedList.removeNode(node);
        doubleLinkedList.addHead(node);

        return node.value;
    }

    public void put(int key, int value) {
        // 如果存在,则替换新的value,再放回map
        if (map.containsKey(key)) {
            Node node = map.get(key);
            node.value = value;
            map.put(key, node);

            doubleLinkedList.removeNode(node);
            doubleLinkedList.addHead(node);
        } else {
            // 坑位满了
            if (map.size() == capacity) {
                Node lastNode = doubleLinkedList.getLast();
                map.remove(lastNode.key);
                doubleLinkedList.removeNode(lastNode);
            }

            // 坑位没满,新增
            Node newNode = new Node<>(key, value);
            map.put(key, newNode);
            doubleLinkedList.addHead(newNode);
        }
    }
    
}

你可能感兴趣的:(算法lrucache)