Loading...

LRU缓存淘汰算法Java实现

在这里插入图片描述

LRU的核心思想是当缓存空间满了,优先淘汰最久未被使用的数据。

这种思想其实在生活中也有,比如想象一下你的书桌只能放5本书。每次看完一本书,你就把它放在最右边。当需要放新书但桌面满了时,你会扔掉最左边那本(最久没看的)。这其实就是LRU的思想!

代码逻辑

1. addNode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public void addNode(DoubleNode newNode) {
    if (head == null) {
        // 链表为空,新节点既是头也是尾
        head = newNode;
        tail = newNode;
    } else {
        // 追加到尾部
        tail.next = newNode;
        newNode.last = tail;
        tail = newNode;
    }
}

2. moveNodeToTail

每次访问一个节点,都要把它移到尾部,表示"最近刚用过"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public void moveNodeToTail(DoubleNode node) {
    if (tail == node) return;  // 已经在尾部
    
    // 从原位置摘除节点
    if (head == node) {
        head = node.next;
        head.last = null;
    } else {
        node.last.next = node.next;
        node.next.last = node.last;
    }
    
    // 追加到尾部
    node.last = tail;
    node.next = null;
    tail.next = node;
    tail = node;
}

3. removeHead

当缓存满了需要淘汰数据时,删除头部(最久未使用的)节点。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public DoubleNode removeHead() {
    if (head == null) return null;
    
    DoubleNode ans = head;
    if (head == tail) {
        // 只有一个节点
        head = null;
        tail = null;
    } else {
        head = ans.next;
        ans.next = null;
        head.last = null;
    }
    return ans;
}

4.get - 查询数据

1
2
3
4
5
6
7
8
public int get(int key) {
    if (keyNodeMap.containsKey(key)) {
        DoubleNode ans = keyNodeMap.get(key);
        nodeList.moveNodeToTail(ans);  // 标记为最近使用
        return ans.val;
    }
    return -1;
}

5.put - 插入或更新数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public void put(int key, int value) {
    if (keyNodeMap.containsKey(key)) {
        // key已存在,更新value
        DoubleNode node = keyNodeMap.get(key);
        node.val = value;
        nodeList.moveNodeToTail(node);  // 移到尾部
    } else {
        // key不存在,插入新节点
        if (keyNodeMap.size() == capacity) {
            // 缓存已满,先淘汰最久未使用的
            DoubleNode removed = nodeList.removeHead();
            keyNodeMap.remove(removed.key);
        }
        
        DoubleNode newNode = new DoubleNode(key, value);
        keyNodeMap.put(key, newNode);
        nodeList.addNode(newNode);
    }
}

完整代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
class LRUCache{
		class DoubleNode {
			public int key;      // 键
			public int val;      // 值
			public DoubleNode last;  // 前驱节点
			public DoubleNode next;  // 后继节点
			public DoubleNode(int k, int v) {
				key = k;
				val = v;
			}
		}

		class DoubleList{
			private DoubleNode head;
			private DoubleNode tail;

			public DoubleList(){
				head = null;
				tail = null;
			}

			public void addNode(DoubleNode newNode) {
				if (head == null) {
					// 链表为空,新节点既是头也是尾
					head = newNode;
					tail = newNode;
				} else {
					// 追加到尾部
					tail.next = newNode;
					newNode.last = tail;
					tail = newNode;
				}
			}

			public void moveNodeToTail(DoubleNode node) {
				if (tail == node) return;  // 已经在尾部

				// 第一步: 从原位置摘除节点
				if (head == node) {
					head = node.next;
					head.last = null;
				} else {
					node.last.next = node.next;
					node.next.last = node.last;
				}

				// 第二步: 追加到尾部
				node.last = tail;
				node.next = null;
				tail.next = node;
				tail = node;
			}

			public DoubleNode removeHead() {
				if (head == null) return null;

				DoubleNode ans = head;
				if (head == tail) {
					// 只有一个节点
					head = null;
					tail = null;
				} else {
					head = ans.next;
					ans.next = null;
					head.last = null;
				}
				return ans;
			}
		}

		private HashMap<Integer, DoubleNode> keyNodeMap;
		private DoubleList nodeList;

		private final int capacity;

		public LRUCache(int cap){
			keyNodeMap = new HashMap<>();
			nodeList = new DoubleList();
			capacity = cap;
		}

		public int get(int key) {
			if (keyNodeMap.containsKey(key)) {
				DoubleNode ans = keyNodeMap.get(key);
				nodeList.moveNodeToTail(ans);  // 标记为最近使用
				return ans.val;
			}
			return -1;
		}

		public void put(int key, int value) {
			if (keyNodeMap.containsKey(key)) {
				// key已存在,更新value
				DoubleNode node = keyNodeMap.get(key);
				node.val = value;
				nodeList.moveNodeToTail(node);  // 移到尾部
			} else {
				// key不存在,插入新节点
				if (keyNodeMap.size() == capacity) {
					// 缓存已满,先淘汰最久未使用的
					DoubleNode removed = nodeList.removeHead();
					keyNodeMap.remove(removed.key);
				}
				DoubleNode newNode = new DoubleNode(key, value);
				keyNodeMap.put(key, newNode);
				nodeList.addNode(newNode);
			}
		}

	}

关注我,持续分享算法干货! 🚀

最后更新于 2026-04-05 17:35:33
Code Road Record