Java實現LRU Cache的兩種方法

方法一

完全使用Java的LinkedHashMap來實現。實現也有兩種方法,一種使用繼承,另一種使用組合,這裏給出使用組合的代碼。

public class LRUCache<K, V> {
    private Map<K, V> map;
    private final int cacheSize;

    public LRUCache(int initialCapacity) {
        map = new LinkedHashMap<K, V>(initialCapacity, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                return size() > cacheSize;
            }
        };
        this.cacheSize = initialCapacity;
    }
}

核心部分在LinkedHashMap的第三個構造函數上,要把這個構造參數accessOrder設爲true,代表LinkedHashMap內部維持訪問順序。另外,還需要重寫removeEldestEntry(),這個函數如果返回true,代表應該把最久未被訪問的節點移除。

這裏記錄一下LinkedHashMap的三個方法:

  • void afterNodeAccess(Node<K,V> p) {}
  • void afterNodeInsertion(boolean evict) {}
  • void afterNodeRemoval(Node<K,V> p) {}

這三個方法在HashMap中就有,而且爲空;LinkedHashMap繼承了HashMap,並重寫了這三個方法。下面是LinkedHashMap中這三個函數的作用。

afterNodeAccess方法

作用:將目前訪問的節點放到鏈表最後。

調用位置

  • get方法中,如果accessOrder設爲true,調用該方法。
  • put方法中,如果當前插入的值已經在Map中,調用該方法。
afterNodeInsertion方法

作用:如果removeEldestEntry函數返回true,那麼該方法就把最久未被訪問的節點刪除。

調用位置:在put方法中,如果當前插入的值不在Map中,調用該方法。

afterNodeRemoval方法

作用:移除節點後,維護該節點前後雙向鏈表的關係。

調用位置Mapremove方法中

方法二

自己實現。其中代碼是從 LeetCode 146. LRU Cache 上摘下來的。代碼裏面有註釋。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 在鏈頭放最久未被使用的元素,鏈尾放剛剛添加或訪問的元素
 */
class LRUCache {
    class Node {
        int key, value;
        Node pre, next;

        Node(int key, int value) {
            this.key = key;
            this.value = value;
            pre = this;
            next = this;
        }
    }

    private final int capacity;// LRU Cache的容量
    private Node dummy;// dummy節點是一個冗餘節點,dummy的next是鏈表的第一個節點,dummy的pre是鏈表的最後一個節點
    private Map<Integer, Node> cache;//保存key-Node對,Node是雙向鏈表節點

    public LRUCache(int capacity) {
        this.capacity = capacity;
        dummy = new Node(0, 0);
        cache = new ConcurrentHashMap<>();
    }

    public int get(int key) {
        Node node = cache.get(key);
        if (node == null) return -1;
        remove(node);
        add(node);
        return node.value;
    }

    public void put(int key, int value) {
        Node node = cache.get(key);
        if (node == null) {
            if (cache.size() >= capacity) {
                cache.remove(dummy.next.key);
                remove(dummy.next);
            }
            node = new Node(key, value);
            cache.put(key, node);
            add(node);
        } else {
            cache.remove(node.key);
            remove(node);
            node = new Node(key, value);
            cache.put(key, node);
            add(node);
        }
    }

    /**
     * 在鏈表尾部添加新節點
     *
     * @param node 新節點
     */
    private void add(Node node) {
        dummy.pre.next = node;
        node.pre = dummy.pre;
        node.next = dummy;
        dummy.pre = node;
    }

    /**
     * 從雙向鏈表中刪除該節點
     *
     * @param node 要刪除的節點
     */
    private void remove(Node node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章