Java 实现LRU算法

176 阅读1分钟

/**
 * 相当于同时维护了一个Map用来存储缓存内容,一个双向链表用来维护访问顺序
 * @param <K>
 * @param <V>
 */
public class Cache<K, V> {

    class Node {
        Node pre;
        Node next;
        K key;
        V value;
    }

    /**
     * 缓存结构,通过K查询,Node自身维护LRU的访问顺序
     */
    private final HashMap<K, Node> nodeCache;

    private int maxCapacity;

    private Node first;

    private Node last;

    public Cache(int maxCapacity) {
        this.nodeCache = new HashMap<>(maxCapacity);
        this.maxCapacity = maxCapacity;
    }

    public void put(K k, V v) {
        Node node = nodeCache.get(k);
        //如果不存在就添加
        if (node == null) {
            //判断缓存容量
            if (nodeCache.size() >= maxCapacity) {
                //移除最后一个
                nodeCache.remove(last.key);
                //更新链表节点顺序
                removeLast();
            }
            node = new Node();
            node.key = k;
        }
        //如果存在就put更新
        node.value = v;
        //不管存不存在,该节点都要更新为first,表示最近访问过
        freshFirst(node);
        nodeCache.put(k, node);
    }

    public void remove(K k, V v) {

        Node node = nodeCache.get(k);
        if (node == null) {
            return;
        }
        if (node.equals(last)) {
            last = last.pre;
        }
        if (node.equals(first)) {
            first = first.next;
        }
        if (node.next != null) {
            node.next.pre = node.pre;
        }
        if (node.pre != null) {
            node.pre.next = node.next;
        }
        nodeCache.remove(k);
    }


    private void freshFirst(Node node) {

        if (first.equals(node)) {
            return;
        }
        //put是更新操作时,node本身就在链表中,需要挪动它的位置
        if (node.next != null) {
            node.next.pre = node.pre;
        }
        if (node.pre != null) {
            node.pre.next = node.next;
        }
        if (node.equals(last)) {
            last = last.pre;
        }
        //如果链表原本没节点,更新first/last都指向刚加入的node
        if (first == null || last == null) {
            first = last = node;
            return;
        }
        //更新first
        node.next = first;
        first.pre = node;
        first = node;
        first.pre = null;
    }

    private void removeLast() {
        if (last != null) {
            last = last.pre;
            if (last == null) {
                first = null;
            } else {
                last.next = null;
            }
        }
    }
}