一、LinkedHashMap
LinkedHashMap 内部维护了一个双向链表,用来维护插入顺序或者 LRU(最近最久未使用)顺序。
其中有一个属性——accessOrder,当一个节点被访问时,如果 accessOrder 为 true,则会将该节点移到链表尾部。也就是说指定为 LRU 顺序之后,在每次访问一个节点时,会将这个节点移到链表尾部,保证链表尾部是最近访问的节点,那么链表首部就是最近最久未使用的节点。
在 put 等操作之后执行,当 removeEldestEntry()方法返回 true 时(即 size > threshold)会移除最晚的结点,即链表的首部结点。
以下是使用 LinkedHashMap 实现的一个 LRU 缓存:
- 设定最大缓存空间 threshold 为4;
- 使用 LinkedHashMap 的构造函数将 accessOrder 设置为 true,开 LRU 顺序;
- 覆盖 removeEldestEntry()方法实现,在结点多于 threshold 时就会将最近最久未使用的数据移除。
public class LRU<K, V> extends LinkedHashMap<K, V> {
private int initialSize;
public LRU(int initialSize) {
super(initialSize, 0.75f, true);
this.initialSize = initialSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return this.size() > initialSize;
}
public String toString() {
StringBuilder cacheStr = new StringBuilder();
cacheStr.append("{");
for (Map.Entry<K, V> entry : this.entrySet()) {
cacheStr.append("[" + entry.getKey() + "," + entry.getValue() + "]");
}
cacheStr.append("}");
return cacheStr.toString();
}
public static void main(String[] args) {
LRU<Integer, Integer> cache = new LRU<>(3);
cache.put(1, 111);
cache.put(2, 222);
cache.put(3, 333);
cache.get(2);
cache.put(4, 444);
System.out.println(cache.toString());
}
}
二、双向链表 + HashMap
public class LRU {
static HashMap<Integer, Node> map = new HashMap<>();
static Node head = new Node();
static Node end = null;
static int capacity = 4;
public static void tranverse() { // 遍历链表
Node phead = head.next;
while (phead != null) {
System.out.print("(" + phead.key + "," + phead.value + ")->");
phead = phead.next;
}
System.out.println();
}
public static int get(int key) {
if (map.containsKey(key)) {
Node node = map.get(key);
remove(node);
setHead(node); // 访问结点时,将其置为头结点
return node.value;
}
return -1;
}
public static void remove(Node node) {
if (null == node.next) {
node.pre.next = null;
} else {
node.pre.next = node.next;
node.next.pre = node.pre;
}
}
public static void setHead(Node node) {
if (head.next != null) {
node.next = head.next;
head.next.pre = node;
head.next = node;
node.pre = head;
} else {
head.next = node;
node.pre = head;
}
}
public static void put(int key, int value) {
if (map.containsKey(key)) {
Node old = map.get(key);
old.value = value;
remove(old);
setHead(old);
} else {
Node newCode = new Node(key, value);
map.put(key, newCode);
if (head.next != null) { // 找到end所在结点
end = head.next;
while (end.next != null) {
end = end.next;
}
}
if (map.size() > capacity) // 当size >= capacity时移除end结点
if (end.key != -1) {
map.remove(end.key);
remove(end);
setHead(newCode);
} else {
setHead(newCode);
}
} else {
setHead(newCode);
}
}
}
}
class Node { // 双向链表
int key = -1;
int value = -1;
Node pre = null;
Node next = null;
public Node() {
}
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}