Skip to content

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

  • 原始路径:labs/foundations/cache-eviction-demo/src/LruLinkedHashMapDemo.java
  • 类型:java
java
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 演示 LinkedHashMap 如何用 accessOrder + removeEldestEntry 实现 LRU。
 *
 * 理论:docs/00-foundations/04-lru-cache-and-eviction-policies.md
 *
 * 运行:
 * cd labs/foundations/cache-eviction-demo
 * javac -d out src/LruLinkedHashMapDemo.java
 * java -cp out LruLinkedHashMapDemo
 */
public class LruLinkedHashMapDemo {

    static final int MAX_ENTRIES = 3;

    public static void main(String[] args) {
        System.out.println("====== LinkedHashMap 实现 LRU(容量 = " + MAX_ENTRIES + ")======\n");
        System.out.println("规则:accessOrder=true → 迭代顺序 = 最久未用 → 最近使用");
        System.out.println("      put 后若 size > MAX,removeEldestEntry 删掉链表头(最久未用)\n");

        LruMap<String, String> lru = new LruMap<>();

        step(lru, "put A", () -> lru.put("A", "apple"));
        step(lru, "put B", () -> lru.put("B", "banana"));
        step(lru, "put C", () -> lru.put("C", "cherry"));
        step(lru, "put D(满员,应踢 A)", () -> lru.put("D", "date"));
        step(lru, "get B(B 变最近使用)", () -> lru.get("B"));
        step(lru, "put E(应踢 C,因 B 刚被访问、C 最久)", () -> lru.put("E", "elderberry"));

        System.out.println("====== 续:D-B-E 后反复 get D,再 put F ======\n");
        for (int i = 0; i < 5; i++) {
            lru.get("D");
        }
        step(lru, "get D ×5(D 已在队尾,顺序不变)", () -> {
        });
        step(lru, "put F(应踢 B,不是 D)", () -> lru.put("F", "fig"));

        System.out.println("\n====== 手写 LRU:HashMap + 双向链表(见 SimpleLruCache.java)======\n");
        System.out.println("运行: java -cp out SimpleLruCache");
        System.out.println();

        System.out.println("====== 对比:accessOrder=false(插入顺序,不是 LRU)======\n");
        demoInsertionOrderNotLru();
    }

    static void step(LruMap<String, String> lru, String action, Runnable op) {
        System.out.println("--- " + action + " ---");
        op.run();
        System.out.println("  当前 key 顺序(左=最久未用,右=最近使用): " + lru.orderKeys());
        System.out.println("  当前 map: " + lru);
        System.out.println();
    }

    /** accessOrder=false 时,「最老」= 最早插入,get 不会把条目挪到尾部 */
    static void demoInsertionOrderNotLru() {
        LinkedHashMap<String, String> fifoStyle = new LinkedHashMap<>(16, 0.75f, false) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
                return size() > MAX_ENTRIES;
            }
        };
        fifoStyle.put("A", "1");
        fifoStyle.put("B", "2");
        fifoStyle.put("C", "3");
        fifoStyle.get("A");
        fifoStyle.put("D", "4");
        System.out.println("put A,B,C → get A → put D");
        System.out.println("  淘汰的是链表头(最早插入): 剩 " + fifoStyle.keySet());
        System.out.println("  若先 get 的是 C,put D 仍会踢 A —— 与「按访问」的 LRU 行为不同");
    }

    static class LruMap<K, V> extends LinkedHashMap<K, V> {
        LruMap() {
            super(16, 0.75f, true);
        }

        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            boolean evict = size() > MAX_ENTRIES;
            if (evict) {
                System.out.println("  >> removeEldestEntry: 淘汰最久未用 key=" + eldest.getKey());
            }
            return evict;
        }

        String orderKeys() {
            return entrySet().stream()
                    .map(e -> String.valueOf(e.getKey()))
                    .collect(Collectors.joining(" → "));
        }
    }
}