-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathLRUCache.java
More file actions
168 lines (141 loc) · 4.77 KB
/
LRUCache.java
File metadata and controls
168 lines (141 loc) · 4.77 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import java.util.HashMap;
import java.util.Map;
/**
* Copyright © https://github.com/microwind All rights reserved.
* @author: jarryli@gmail.com
* @version: 1.0
* @description:
* LRU (Least Recently Used) 缓存实现(Java)
* 功能:使用哈希表+双向链表实现O(1) get/put操作的缓存
* 用途:学习经典算法题,理解缓存淘汰策略和链表应用
*
* 核心操作:
* - get(key): 获取值并将节点移到最近使用位置
* - put(key, value): 插入新值,满时淘汰最久未使用项
*/
public class LRUCache {
private final int capacity;
private final Map<Integer, Node> map;
private final Node head, tail;
private static class Node {
int key, val;
Node prev, next;
Node(int k, int v) {
key = k;
val = v;
}
}
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>(capacity);
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.prev = head;
}
public int get(int key) {
Node node = map.get(key);
if (node == null)
return -1;
moveToHead(node);
return node.val;
}
public void put(int key, int value) {
Node node = map.get(key);
if (node != null) {
node.val = value;
moveToHead(node);
} else {
if (map.size() == capacity) {
Node lru = tail.prev;
removeNode(lru);
map.remove(lru.key);
}
Node newNode = new Node(key, value);
addNode(newNode);
map.put(key, newNode);
}
}
private void addNode(Node node) {
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
private void removeNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(Node node) {
removeNode(node);
addNode(node);
}
public static void main(String[] args) {
System.out.println("=== LRU Cache 测试 ===");
// 创建容量为2的LRU缓存
LRUCache lruCache = new LRUCache(2);
System.out.println("1. 测试put操作:");
lruCache.put(1, 1);
System.out.println("put(1, 1) - 缓存内容: " + getCacheContent(lruCache));
lruCache.put(2, 2);
System.out.println("put(2, 2) - 缓存内容: " + getCacheContent(lruCache));
System.out.println("\n2. 测试get操作:");
int result1 = lruCache.get(1);
System.out.println("get(1) = " + result1 + " - 缓存内容: " + getCacheContent(lruCache));
System.out.println("\n3. 测试缓存满时的LRU淘汰:");
lruCache.put(3, 3);
System.out.println("put(3, 3) - 缓存内容: " + getCacheContent(lruCache));
System.out.println("注意: 键2被淘汰,因为键1最近被访问过");
int result2 = lruCache.get(2);
System.out.println("get(2) = " + result2 + " (应该返回-1,因为键2已被淘汰)");
System.out.println("\n4. 测试更新已存在的键:");
lruCache.put(1, 10);
System.out.println("put(1, 10) - 缓存内容: " + getCacheContent(lruCache));
System.out.println("\n5. 测试访问顺序影响淘汰:");
lruCache.put(4, 4);
System.out.println("put(4, 4) - 缓存内容: " + getCacheContent(lruCache));
System.out.println("注意: 键3被淘汰,因为键1最近被更新过");
System.out.println("\n=== 测试完成 ===");
}
// 辅助方法:获取缓存内容的字符串表示(仅用于测试)
private static String getCacheContent(LRUCache cache) {
try {
// 通过反射获取map的内容(仅用于测试)
java.lang.reflect.Field mapField = LRUCache.class.getDeclaredField("map");
mapField.setAccessible(true);
@SuppressWarnings("unchecked")
Map<Integer, Node> map = (Map<Integer, Node>) mapField.get(cache);
StringBuilder sb = new StringBuilder();
sb.append("{");
boolean first = true;
for (Map.Entry<Integer, Node> entry : map.entrySet()) {
if (!first) sb.append(", ");
sb.append(entry.getKey()).append(":").append(entry.getValue().val);
first = false;
}
sb.append("}");
return sb.toString();
} catch (Exception e) {
return "[无法显示缓存内容]";
}
}
}
/**
jarry@Mac map % java LRUCache.java
=== LRU Cache 测试 ===
1. 测试put操作:
put(1, 1) - 缓存内容: {1:1}
put(2, 2) - 缓存内容: {1:1, 2:2}
2. 测试get操作:
get(1) = 1 - 缓存内容: {1:1, 2:2}
3. 测试缓存满时的LRU淘汰:
put(3, 3) - 缓存内容: {1:1, 3:3}
注意: 键2被淘汰,因为键1最近被访问过
get(2) = -1 (应该返回-1,因为键2已被淘汰)
4. 测试更新已存在的键:
put(1, 10) - 缓存内容: {1:10, 3:3}
5. 测试访问顺序影响淘汰:
put(4, 4) - 缓存内容: {4:4, 1:10}
注意: 键3被淘汰,因为键1最近被更新过
=== 测试完成 ===
*/