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,


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章