Java 实现 LRU 缓存:一行代码 vs 手写双向链表(附记忆口诀)

Java中实现LRU缓存的最简方案 是直接使用LinkedHashMap,它通过维护插入顺序或访问顺序的链表,天然支持LRU策略。以下是两种实现方式的对比:

方案1:使用LinkedHashMap(最简实现)

java 复制代码
import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache extends LinkedHashMap<Integer, Integer> {
    private final int capacity;
    
    // 构造函数:设置accessOrder为true,启用访问顺序
    public LRUCache(int capacity) {
        super(capacity, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                return size() > capacity; // 超过容量时自动删除最老元素
            }
        };
        this.capacity = capacity;
    }
    
    public int get(int key) {
        return super.getOrDefault(key, -1); // 不存在返回-1
    }
    
    public void put(int key, int value) {
        super.put(key, value);
    }
}

关键点

  1. LinkedHashMap的构造参数:
    • accessOrder=true:按访问顺序排序(最近访问的元素移到尾部)
    • removeEldestEntry():重写此方法,当大小超过容量时自动删除最老元素
  2. 时间复杂度:get/put 均为 O(1)

方案2:手动实现(面试常考)

如果你需要在面试中手动实现(不依赖LinkedHashMap),则需用哈希表+双向链表

java 复制代码
import java.util.HashMap;
import java.util.Map;

// 双向链表节点
class DNode {
    int key, value;
    DNode prev, next;
    public DNode(int k, int v) { key = k; value = v; }
}

public class LRUCache {
    private Map<Integer, DNode> map;
    private DNode head, tail;
    private int capacity;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        head = new DNode(0, 0);
        tail = new DNode(0, 0);
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        if (!map.containsKey(key)) return -1;
        DNode node = map.get(key);
        removeNode(node); // 从链表中删除
        addToHead(node);  // 移到头部(最近使用)
        return node.value;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            removeNode(map.get(key)); // 已存在则删除
        }
        DNode newNode = new DNode(key, value);
        map.put(key, newNode);
        addToHead(newNode); // 新增到头部
        if (map.size() > capacity) {
            DNode tailNode = removeTail(); // 超容则删除尾部(最久未用)
            map.remove(tailNode.key);
        }
    }

    private void removeNode(DNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void addToHead(DNode node) {
        node.next = head.next;
        node.next.prev = node;
        head.next = node;
        node.prev = head;
    }

    private DNode removeTail() {
        DNode tailNode = tail.prev;
        removeNode(tailNode);
        return tailNode;
    }
}

对比选择

方案 优点 适用场景
LinkedHashMap 代码仅需10行,简洁高效 日常开发、快速实现
手动实现 展示底层原理,面试加分项 面试手撕、深入理解LRU机制

记忆口诀(LinkedHashMap版)

java 复制代码
继承LinkedHashMap,仨参数要写对
accessOrder设为true,删除老元素有回调
get/put直接调父类,LRU缓存就这么简单

关键点

  • LinkedHashMap的三个核心参数:初始容量、负载因子、访问顺序
  • 重写removeEldestEntry()方法控制删除策略

这种方案避免了手动维护双向链表的复杂性,适合快速实现功能。但面试中若要求"不使用任何库",则需手动实现版本。

相关推荐
宵时待雨1 分钟前
回溯算法专题1:递归
数据结构·c++·笔记·算法·leetcode·深度优先
爱思德学术5 分钟前
【SPIE出版】黄冈师范学院主办!第四届大数据、计算智能与应用国际会议(BDCIA 2026)
大数据·算法·数据分析·云计算·etl
洛水水5 分钟前
【力扣100题】40.二叉树中的最大路径和
算法·leetcode·深度优先
洛水水9 分钟前
【力扣100题】37.从前序与中序遍历序列构造二叉树
c++·算法·leetcode
zyq99101_111 分钟前
递归与动态规划实战代码解析
python·算法·蓝桥杯
橘白31632 分钟前
rl笔记(一):策略梯度更新算法推导
人工智能·算法·机器人·强化学习
hhhhhaaa33 分钟前
多节点矩阵式任务系统:统一配置中心与动态规则引擎架构设计
后端·算法·架构
吃着火锅x唱着歌44 分钟前
LeetCode 739.每日温度
算法·leetcode·职场和发展
如竟没有火炬1 小时前
去除重复字母——贪心+单调栈
开发语言·数据结构·python·算法·leetcode·深度优先
薛定e的猫咪1 小时前
【ICML 2025】MODULI:基于扩散模型解锁离线多目标强化学习的偏好泛化
人工智能·学习·算法·机器学习