緩存淘汰策略——最近最久未使用策略(LRU)

什麼是緩存淘汰策略?

緩存位於內存中,而內存的空間很有限,所以緩存也有一個能使用的最大空間,當緩存中的數據超過這個最大空間時,就要使用緩存淘汰策略淘汰一些數據,空出空間給其他數據使用。

最近最久未使用策略

最近最久未使用策略,優先淘汰最久未使用的數據,也就是上次被訪問時間距離現在最久的數據。該策略可以保證內存中的數據都是熱點數據,也就是經常被訪問的數據,從而保證緩存命中率。

實現方式

以下是基於 雙向鏈表 + HashMap 的 LRU 算法實現,對算法的解釋如下:

  • 訪問某個節點時,將其從原來的位置刪除,並重新插入到鏈表頭部。這樣就能保證鏈表尾部存儲的就是最近最久未使用的節點,當節點數量大於緩存最大空間時就淘汰鏈表尾部的節點。
  • 爲了使刪除操作時間複雜度爲 O(1),就不能採用遍歷的方式找到某個節點。HashMap 存儲着 Key 到節點的映射,通過 Key 就能以 O(1) 的時間得到節點,然後再以 O(1) 的時間將其從雙向隊列中刪除。
public class LRU<K, V> implements Iterable<K> {

    private Node head;
    private Node tail;
    private HashMap<K, Node> map;
    private int maxSize;

    private class Node {

        Node pre;
        Node next;
        K k;
        V v;

        public Node(K k, V v) {
            this.k = k;
            this.v = v;
        }
    }


    public LRU(int maxSize) {

        this.maxSize = maxSize;
        this.map = new HashMap<>(maxSize * 4 / 3);

        head = new Node(null, null);
        tail = new Node(null, null);

        head.next = tail;
        tail.pre = head;
    }


    public V get(K key) {

        if (!map.containsKey(key)) {
            return null;
        }

        Node node = map.get(key);
        unlink(node);
        appendHead(node);

        return node.v;
    }


    public void put(K key, V value) {

        if (map.containsKey(key)) {
            Node node = map.get(key);
            unlink(node);
        }

        Node node = new Node(key, value);
        map.put(key, node);
        appendHead(node);

        if (map.size() > maxSize) {
            Node toRemove = removeTail();
            map.remove(toRemove.k);
        }
    }


    private void unlink(Node node) {

        Node pre = node.pre;
        Node next = node.next;

        pre.next = next;
        next.pre = pre;

        node.pre = null;
        node.next = null;
    }


    private void appendHead(Node node) {
        Node next = head.next;
        node.next = next;
        next.pre = node;
        node.pre = head;
        head.next = node;
    }


    private Node removeTail() {

        Node node = tail.pre;

        Node pre = node.pre;
        tail.pre = pre;
        pre.next = tail;

        node.pre = null;
        node.next = null;

        return node;
    }


    @Override
    public Iterator<K> iterator() {

        return new Iterator<K>() {
            private Node cur = head.next;

            @Override
            public boolean hasNext() {
                return cur != tail;
            }

            @Override
            public K next() {
                Node node = cur;
                cur = cur.next;
                return node.k;
            }
        };
    }
}

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