LRU缓存的java实现

1. LRU缓存 (Least Recently Used 删除最近未使用缓存)

在get/put时,将当前结点移到头部; 那么尾部自然是最近未使用的key

import java.util.Map;
import java.util.WeakHashMap;


/**
 * LRU缓存 (Least Recently Used 删除最近未使用缓存)
 */
public class LRUCache<TKey, TValue> {

    /**
     * 缓存node结构 (双向链接)
     *
     * @param <TKey>   缓存node的key类型
     * @param <TValue> 缓存node的value类型
     */
    private class Node<TKey, TValue> {
        /**
         * 双向链接node的前一个结点
         */
        private Node<TKey, TValue> prev;

        /**
         * 双向链接node的后一个结点
         */
        private Node<TKey, TValue> next;

        /**
         * 缓存的key
         */
        private TKey key;

        /**
         * 缓存的value
         */
        private TValue value;

        public Node() {
        }

        public Node(TKey key, TValue value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "key=" + key +
                    ", value=" + value +
                    '}';
        }
    }

    /**
     * 增加Map来存储具体缓存项,来实现O(1)访问
     */
    private final Map<TKey, Node<TKey, TValue>> cache = new WeakHashMap<>();

    /**
     * 缓存最大数量
     */
    private final int capacity;
    /**
     * 当前缓存数量
     */
    private int size;

    /**
     * 双向链接的header
     */
    private final Node<TKey, TValue> header;

    /**
     * 双向链接的tail
     */
    private final Node<TKey, TValue> tail;

    /**
     * 获取缓存
     *
     * @param key 缓存key
     * @return 缓存value
     */
    public TValue get(final TKey key) {
        Node<TKey, TValue> node = cache.get(key);
        if (node == null) {
            return null;
        }
        // 移到header
        synchronized (cache) {
            move2Header(node);
        }

        return node.value;
    }

    /**
     * 添加数据
     *
     * @param key   缓存key
     * @param value 缓存value
     */
    public void put(final TKey key, final TValue value) {
        Node<TKey, TValue> node = cache.get(key);
        if (node == null) {
            synchronized (cache) {
                if (size >= capacity) {
                    //删除尾部
                    Node<TKey, TValue> expireNode = removeTail();
                    cache.remove(expireNode.key);
                    this.size--;
                }

                node = insertHeader(key, value);
                cache.put(key, node);
                this.size++;
            }
        } else {
            node.value = value;
            synchronized (cache) {
                move2Header(node);
            }
        }
    }

    /**
     * 移除缓存
     *
     * @param key 缓存key
     */
    public void remove(final TKey key) {
        Node<TKey, TValue> node = cache.get(key);
        if (node == null) {
            return;
        }
        synchronized (cache) {
            remove(node);
            this.cache.remove(key);
            this.size--;
        }
    }

    /**
     * 添加缓存key/value到链表的header
     *
     * @param key   缓存key
     * @param value 缓存value
     */
    private Node<TKey, TValue> insertHeader(final TKey key, final TValue value) {
        Node<TKey, TValue> node = new Node<>(key, value);

        return insertHeader(node);
    }

    /**
     * 添加缓存node到链表的header
     *
     * @param node 缓存node
     * @return 缓存node
     */
    private Node<TKey, TValue> insertHeader(final Node<TKey, TValue> node) {
        node.prev = this.header;
        node.next = this.header.next;
        this.header.next.prev = node;
        this.header.next = node;
        return node;
    }

    /**
     * 移动缓存node到header
     *
     * @param node 缓存node
     */
    private void move2Header(final Node<TKey, TValue> node) {
        remove(node);
        insertHeader(node);
    }

    /**
     * 删除链表尾部
     */
    private Node<TKey, TValue> removeTail() {
        Node<TKey, TValue> node = this.tail.prev;
        remove(node);
        return node;
    }

    /**
     * 删除链表的node
     */
    private void remove(final Node<TKey, TValue> node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    /**
     * 添加缓存node到链表的tail
     *
     * @param key   缓存node的key
     * @param value 缓存node的value
     * @return 缓存node
     */
    private Node<TKey, TValue> putTail(final TKey key, final TValue value) {
        Node<TKey, TValue> node = new Node<>(key, value);
        return putTail(node);
    }

    /**
     * 添加缓存node到链表的tail
     *
     * @param node 缓存node
     * @return 缓存node
     */
    private Node<TKey, TValue> putTail(final Node<TKey, TValue> node) {
        node.prev = this.tail.prev;
        node.next = this.tail;
        this.tail.prev.next = node;
        this.tail.prev = node;
        return node;
    }

    /**
     * .ctor
     *
     * @param capacity 最大缓存数量
     */
    public LRUCache(final int capacity) {
        this.capacity = capacity;
        this.header = new Node<>();
        this.tail = new Node<>();
        this.header.next = this.tail;
        this.header.prev = null;
        this.tail.prev = this.header;
        this.tail.next = null;
        this.size = 0;
    }

    @Override
    public String toString() {
        Node<TKey, TValue> node = this.header.next;
        StringBuilder sb = new StringBuilder();
        sb.append("(size:");
        sb.append(size);
        sb.append(")");
        while (node != this.tail) {
            sb.append(node.key);
            sb.append(":");
            sb.append(node.value);
            sb.append(",");
            node = node.next;
        }
        // sb.append(cache.toString());
        return sb.toString();
    }
}

 

2.测试. 

    public static void main(String[] argv) {
        LRUCache<String, String> cache = new LRUCache<>(4);
        cache.put("key1", "value1");
        System.out.println(cache);
        cache.put("key2", "value2");
        System.out.println(cache);
        cache.put("key3", "value3");
        System.out.println(cache);
        cache.put("key4", "value4");
        System.out.println(cache);
        cache.put("key4", "value44");
        System.out.println(cache);
        cache.put("key5", "value5");
        System.out.println(cache);
    }

输出:

(size:1)key1:value1,
(size:2)key2:value2,key1:value1,
(size:3)key3:value3,key2:value2,key1:value1,
(size:4)key4:value4,key3:value3,key2:value2,key1:value1,
(size:4)key4:value44,key3:value3,key2:value2,key1:value1,
(size:4)key5:value5,key4:value44,key3:value3,key2:value2,


 

posted @ 2020-10-13 18:18  zslm___  阅读(440)  评论(0编辑  收藏  举报