Skip to content

源码:labs/foundations/cache-eviction-demo/src/SimpleLruCache.java

  • 原始路径:labs/foundations/cache-eviction-demo/src/SimpleLruCache.java
  • 类型:java
java
import java.util.HashMap;
import java.util.Map;

/**
 * 手写 LRU:HashMap + 双向链表,不依赖 LinkedHashMap。
 *
 * 链表方向约定(与 printOrder 一致):
 * head … 最久未用(LRU,先淘汰) … 最近使用(MRU) … tail
 * - 淘汰:从 head.next 移除
 * - 插入/访问:linkBefore(tail, node)
 *
 * 理论:docs/00-foundations/04-lru-cache-and-eviction-policies.md
 *
 * 运行:
 * cd labs/foundations/cache-eviction-demo
 * javac -d out src/SimpleLruCache.java && java -cp out SimpleLruCache
 */
public class SimpleLruCache {

    static final int CAPACITY = 3;

    public static void main(String[] args) {
        System.out.println("====== 手写 LRU(HashMap + 双向链表)======\n");

        SimpleLruCache cache = new SimpleLruCache(CAPACITY);

        cache.put("A", "apple");
        cache.put("B", "banana");
        cache.put("C", "cherry");
        cache.printOrder("put A,B,C");

        cache.put("D", "date");
        cache.printOrder("put D(踢最左 A)");

        cache.get("B");
        cache.printOrder("get B(moveToTail)");

        for (int i = 0; i < 5; i++) {
            cache.get("D");
        }
        cache.printOrder("get D ×5(已在队尾,顺序不变)");

        cache.put("F", "fig");
        cache.printOrder("put F(踢最左 C,不是 D)");
    }

    static class Node {
        final String key;
        String value;
        Node prev;
        Node next;

        Node(String key, String value) {
            this.key = key;
            this.value = value;
        }
    }

    private final int capacity;
    private final Map<String, Node> map = new HashMap<>();
    /**
     * 哑头 / 哑尾:不存真实数据,只作哨兵,方便头尾 O(1) 插删。
     * head 侧 = 最久未用,满员时 evict head.next;
     * tail 侧 = 最近使用,新 put / get 命中后都接到 tail 前。
     */
    private final Node head = new Node("", "");
    private final Node tail = new Node("", "");

    SimpleLruCache(int capacity) {
        this.capacity = capacity;
        head.next = tail;
        tail.prev = head;
    }

    String get(String key) {
        Node node = map.get(key);
        if (node == null) {
            return null;
        }
        moveToTail(node);
        return node.value;
    }

    /** 已存在:更新并挪到 tail(算使用);新 key:插到 tail 前,满员则踢 head.next */
    void put(String key, String value) {
        Node node = map.get(key);
        if (node != null) {
            node.value = value;
            moveToTail(node);
            return;
        }
        Node created = new Node(key, value);
        map.put(key, created);
        linkBefore(tail, created);
        if (map.size() > capacity) {
            Node eldest = head.next;
            unlink(eldest);
            map.remove(eldest.key);
            System.out.println("  >> evict LRU key=" + eldest.key);
        }
    }

    void moveToTail(Node node) {
        unlink(node);
        linkBefore(tail, node);
    }

    void unlink(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    void linkBefore(Node anchor, Node node) {
        node.prev = anchor.prev;
        node.next = anchor;
        anchor.prev.next = node;
        anchor.prev = node;
    }

    void printOrder(String label) {
        StringBuilder sb = new StringBuilder();
        for (Node p = head.next; p != tail; p = p.next) {
            if (sb.length() > 0) {
                sb.append(" → ");
            }
            sb.append(p.key);
        }
        System.out.println(label + " | 顺序(左=最久未用): " + sb);
    }
}