缓存空间的容量是有限的,当有新的数据要进入缓存空间时,如果缓存空间不足就必须按照缓存淘汰策略淘汰掉一些数据。
常用的缓存淘汰策略有:
- 先进先出(FIFO):最先进入的内容作为淘汰对象
- 最近最少使用(LFU):最近最少使用的内容作为淘汰对象
- 最久未使用(LRU):最久没有访问的内容作为淘汰对象
- 非最近使用(NMRU):在最近没有使用的内容中随机选择一个作为淘汰对象
在 Java 中实现 LRU 可以结合 HashMap 和双向链表实现,也可以基于 LinkedHashMap 实现。
结合 HashMap 和双向链表实现 LRU
import java.util.HashMap;
import java.util.Map;
/**
 * 结合 HashMap 和双向链表实现 LRU 缓存策略
 */
public class LRUCacheBasedOnHashMap {
    Map<Integer, Node> cache = new HashMap();
    int cacheCapacity;
    Node head;
    Node tail;
    public LRUCacheBasedOnHashMap(int cacheCapacity) {
        this.cacheCapacity = cacheCapacity;
    }
    // 追加尾部节点
    private void appendTail(Node node) {
        if (cache.size() == 0) {
            head = node;
            tail = node;
        } else {
            node.prev = tail;
            tail.next = node;
            tail = node;
            tail.next = null;
        }
    }
    // 从链表中移除节点
    private void removeNodeFromChain(Node node) {
        // 如果当前节点就是 head,将 head 设为当前节点的下一个
        if (node == head) {
            head = node.next;
        }
        // 如果当前节点就是 tail,将 tail 设为当前节点的上一个
        if (node == tail) {
            tail = node.prev;
        }
        // 如果前一个不是 null,将前一个的 next 设为当前节点的 next
        if (node.prev != null) {
            node.prev.next = node.next;
        }
        // 如果后一个不是 null,将后一个的 prev 设为当前节点的 prev
        if (node.next != null) {
            node.next.prev = node.prev;
        }
    }
    private void handleContainsKey(Node node) {
        if (node == tail) {
            // nothing to do
        } else if (node == head) {
            removeNodeFromChain(node);
            appendTail(node);
        } else {
            removeNodeFromChain(node);
            appendTail(node);
        }
    }
    public void put(int key, int value) {
        if (cache.containsKey(key)) {
            Node node = cache.get(key);
            handleContainsKey(node);
        } else {
            // 达到缓存容量极限,删除头部节点,腾出空间
            if (cache.size() == cacheCapacity) {
                cache.remove(head.key);
                removeNodeFromChain(head);
            }
            Node node = new Node(key, value);
            appendTail(node);
            cache.put(key, node);
        }
        listCache();
    }
    public Node get(int key) {
        if (cache.containsKey(key)) {
            Node node = cache.get(key);
            handleContainsKey(node);
            listCache();
            return node;
        }
        return null;
    }
    public void remove(int key) {
        if (cache.containsKey(key)) {
            Node node = cache.get(key);
            removeNodeFromChain(node);
            cache.remove(key);
        }
    }
    private void listCache() {
        Node currentNode = head;
        System.out.print("当前缓存的key:");
        while (currentNode != null) {
            System.out.print(currentNode.key + " ");
            currentNode = currentNode.next;
        }
        System.out.println();
    }
    public static void main(String[] args) {
        int capacity = 4;
        LRUCacheBasedOnHashMap lru = new LRUCacheBasedOnHashMap(capacity);
        lru.put(1, 1);
        lru.put(2, 2);
        lru.put(3, 3);
        lru.put(4, 4);
        lru.get(1);
        lru.put(5, 5);
        lru.put(6, 6);
        lru.get(5);
    }
}
class Node {
    int key;
    int value;
    Node prev;
    Node next;
    public Node(int key, int value) {
        this.key = key;
        this.value = value;
        this.prev = null;
        this.next = null;
    }
}基于 LinkedHashMap 实现 LRU
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
 * 基于 LinkedHashMap 实现 LRU 缓存策略
 */
public class LRUCacheBasedOnLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
    // 当缓存空间满了,删除访问时间最早的数据
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > cacheCapacity;
    }
    int cacheCapacity;
    public LRUCacheBasedOnLinkedHashMap(int cacheCapacity) {
        // 设置 accessOrder = true 开启按访问顺序排序
        super(16, 0.75f, true);
        this.cacheCapacity = cacheCapacity;
    }
    public V put(K key, V value) {
        super.put(key, value);
        listCache(this);
        return value;
    }
    public V get(Object key) {
        V value = super.get(key);
        listCache(this);
        return value;
    }
    public static void listCache(Map map) {
        Set set = map.keySet();
        System.out.print("当前缓存的key:");
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next().toString() + " ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        int capacity = 4;
        LRUCacheBasedOnLinkedHashMap lru = new LRUCacheBasedOnLinkedHashMap(capacity);
        lru.put(1, 1);
        lru.put(2, 2);
        lru.put(3, 3);
        lru.put(4, 4);
        lru.get(1);
        lru.put(5, 5);
        lru.put(6, 6);
        lru.get(5);
    }
}
class Node {
    int key;
    int value;
    Node prev;
    Node next;
    public Node(int key, int value) {
        this.key = key;
        this.value = value;
        this.prev = null;
        this.next = null;
    }
}