LRU从0到1

133 阅读2分钟

LRU是什么

LRU全名为Least Recently Used,直译成中文为“最近最少使用”,顾名思义,它的作用就是对缓存中的数据进行管理,将最近最少使用的数据清除,以保证缓存的命中率。这种算法的思想是基于“记忆力衰退”原理,即最近没有被访问的数据很可能在不久的将来也不会被访问。

假设你是一个小学生,每天你都要背诵一些东西,比如英语单词。但是你的脑袋只能记住一定数量的单词,当你脑海中的单词已经超过了这个数量时,就需要将一些没有用到过的单词移除,以便为新的单词腾出位置。这个过程就是LRU算法的思想。

自己设计一个LRU

设计要素

  • 有限的空间(比如设置固定的CAP_SIZE)
  • 双向链表
  • map(存储key,value,通过O(1)复杂度查询)

代码

package algo;

import java.util.HashMap;
import java.util.Map;

public class LRU<K, V> {

    private final int CAP_SIZE;
    private Map<K, Node<K, V>> map;
    private Node<K, V> head;
    private Node<K, V> tail;

    public LRU(int size) {
        this.CAP_SIZE = size;
        this.map = new HashMap<>();
        this.head = null;
        this.tail = null;
    }

    /**
     * 将新节点插入到链表的头部。
     * @param node
     */
    public void addNode(Node<K, V> node) {
        if (head == null) {
            head = node;
            tail = node;
        } else {
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

    /**
     * 从链表中删除一个节点。
     * @param node
     */
    public void removeNode(Node<K, V> node) {
        if (node == head && node == tail) {
            head = null;
            tail = null;
        } else if (node == head) {
            head = node.next;
            head.prev = null;
        } else if (node == tail){
            tail = node.prev;
            tail.next = null;
        } else {
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }
    }

    /**
     * 将新的键值对添加到缓存中。
     * 如果键已经存在,则更新值,将相应的节点移动到链表的头部,并在必要时删除尾节点。
     * 如果键不存在,则添加一个新节点到哈希表和链表的头部,并在必要时删除尾节点。
     * @param key
     * @param value
     */
    public void put(K key, V value) {
        Node<K, V> node = map.get(key);
        if (null == node) {
            node = new Node<>(key, value);
            map.put(key, node);
            if (map.size() > CAP_SIZE) {
                map.remove(tail.key);
                removeNode(tail);
            }
        } else {
            node.val = value;
            removeNode(node);
        }
        addNode(node);
    }

    /**
     * 检索与给定键相关联的值。
     * 如果键不存在于哈希表中,则返回null。
     * 否则,将相应的节点移动到链表的头部,并返回其值。
     * @param key
     * @return
     */
    public V get(K key) {
        Node<K, V> node = map.get(key);
        if (null == node) {
            return null;
        }

        removeNode(node);
        addNode(node);
        return node.val;
    }


    private static class Node<K, V>{
        K key;
        V val;
        Node<K, V> prev;
        Node<K, V> next;
        Node(K key, V val) {
            this.key = key;
            this.val = val;
            prev = null;
            next = null;
        }

    }

    public static void main(String[] args) {
        LRU<String, Integer> cache = new LRU<>(2);
        cache.put("a", 1);
        cache.put("b", 2);
        System.out.println(cache.get("a")); // prints 1
        cache.put("c", 3);
        System.out.println(cache.get("b")); // prints null
        cache.put("d", 4);
        System.out.println(cache.get("a")); // prints null
        System.out.println(cache.get("c")); // prints 3
        System.out.println(cache.get("d")); // prints 4
    }
}