常見面試題之作業系統中的LRU快取機制實現

Chiakiiii發表於2020-08-24

LRU快取機制,全稱Least Recently Used,字面意思就是最近最少使用,是一種快取淘汰策略。換句話說,LRU機制就是認為最近使用的資料是有用的,很久沒用過的資料是無用的,當記憶體滿了就優先刪除很久沒有使用的資料

基於LeetCode146,可以使用雜湊連結串列或者自定義雙端連結串列類+雜湊表兩種方法來實現LRU快取機制。

它應該支援以下操作:獲取資料get和 寫入資料put

獲取資料get(key):如果金鑰 (key) 存在於快取中,則獲取金鑰的值(總是正數),否則返回-1
寫入資料put(key, value):如果金鑰不存在,則寫入其資料值。當快取容量達到上限時,它應該在寫入新資料之前刪除最近最少使用的資料值,從而為新的資料值留出空間。

1. 基於LinkedHashMap實現LRU快取機制

class LRUCache {
    Map<Integer, Integer> map;
    int capacity;
    
    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new LinkedHashMap<>();
    }
    
    public int get(int key) {
        // 若key不存在返回-1
        if(!map.containsKey(key)) return -1;
        // 若key存在則獲取key對應的val
        int val = map.get(key);
        // 更新位置
        put(key, val);
        return val;
    }
    
    public void put(int key, int val) {
        // 若快取命中則先刪除資料在重新放入以更新位置
        if(map.containsKey(key)) {
            map.remove(key);
            map.put(key, val);
        } else {
            // 若快取未命中則先判斷是否達到最大容量
            // 超出容量則刪除最久沒有使用的資料(利用迭代器刪除第一個)
            if (capacity == map.size()) map.remove(map.keySet().iterator().next());
            // 刪除完成後存放新資料
            map.put(key, val);
        }
    }
}

2. 基於DoubleList與HashMap實現LRU快取機制

如果不使用LinkedHashMap,可以自己造輪子,自定義DoubleList類並結合HashMap實現與LinkedHashMap相同的功能。

class LRUCache {
    int capacity;
    DoubleList cache;
    Map<Integer, Node> map;
    
    public LRUCache(int capacity) {
        this.capacity = capacity;
        cache = new DoubleList();
        map = new HashMap<>();
    }
    
    public int get(int key) {
        if(!map.containsKey(key)) return -1;
        int val = map.get(key).val;
        put(key, val);
        return val;
    }
    
    public void put(int key, int val) {
        Node node = new Node(key, val);
        if(map.containsKey(key)) {
            cache.remove(map.get(key));
            cache.addFirst(node);
            map.put(key, node);
        } else {
            if(capacity == cache.size) map.remove(cache.removeLast().key);
            cache.addFirst(node);
            map.put(key, node);
        }
    }
}

class DoubleList {
    Node head, tail;
    int size;
    
    public DoubleList() {
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head.next = tail;
        tail.prev = head;
        size = 0;
    }
    
    public void addFirst(Node node) {
        Node temp = head.next;
        node.next = temp;
        temp.prev = node;
        head.next = node;
        node.prev = head;
        size++;
    }
    
    public void remove(Node node) {
        Node temp = node.prev;
        temp.next = node.next;
        temp.next.prev = temp;
        size--;
    }
    
    public Node removeLast() {
        if (size == 0) return null;
        Node del = tail.prev;
        remove(del);
        return del;
    }
}

class Node {
    int key, val;
    Node prev, next;
    
    public Node(int key, int val) {
        this.key = key;
        this.val = val;
    }
}

相關文章