什么是LRU,参考:LRU算法 缓存淘汰策略
基于LinkedHashMap实现
LinkedHashMap本身就基于双向链表实现,而且就更新和记录最新访问元素,所以基于LinkedListHashMap我们只要重写removeEldestEntry方法即可,该方法的意思是,移除最旧的元素。
import java.util.LinkedHashMap;
import java.util.Map;
/**
* date: 2020/9/27
* description: LRUSimpleCache
*
* @author xiaopihai7256
*/
public class LRUSimpleCache extends LinkedHashMap {
private static final long serialVersionUID = 23094571L;
private final int maxCacheSize;
LRUSimpleCache(int initialCapacity, int maxCacheSize) {
super(initialCapacity, 0.75F, true);
this.maxCacheSize = maxCacheSize;
}
protected boolean removeEldestEntry(Map.Entry eldest) {
return this.size() > this.maxCacheSize;
}
// 简单测试
public static void main(String[] args) {
final var lruCache = new LRUSimpleCache<>(3, 3);
lruCache.put("test1", 1);
lruCache.put("test2", 2);
System.out.println(lruCache.toString());
lruCache.get("test1");
System.out.println(lruCache.toString());
lruCache.put("test3", 3);
lruCache.put("test4", 1);
lruCache.put("test5", 2);
lruCache.put("test4", 2);
System.out.println(lruCache.toString());
}
}
2. 基于双向链表和HashMap实现
import java.util.HashMap;
import java.util.Map;
/**
* date: 2020/9/27
* description: 双向连标+map实现
* @author xiaopihai7256
*/
public class LRUCache {
// 简单测试
public static void main(String[] args) {
LRUCache cache2 = new LRUCache<>(3);
cache2.put("test1", 1);
System.out.println(cache2);
cache2.put("test2", 2);
System.out.println(cache2);
cache2.put("test3", 3);
System.out.println(cache2);
cache2.get("test1");
System.out.println(cache2);
cache2.put("test2", 5);
System.out.println(cache2);
cache2.delete("test2");
System.out.println(cache2);
System.out.println(cache2.getHead());
System.out.println(cache2.getLast());
}
/**
* 首尾标志元素
* lruNode.pre -> last
* lruNode.next -> head
*/
private final Node lruNode;
/**
* hash访问缓存
*/
private final Map> cache;
/**
* LRU size
*/
private final int size;
public LRUCache(int size) {
lruNode = new Node<>(null);
cache = new HashMap<>(size <= 16 ? size : 16, 0.75f);
this.size = size;
}
/**
* 普通获取,被命中元素移动到队尾
* @param key key
* @return T value
*/
public T get(K key) {
Node node = cache.get(key);
if (node == null) {
return null;
} else {
if (node != lruNode.pre) {
removeFromChain(node);
appendToChain(node);
}
return node.value;
}
}
// 添加或者更新元素,被操作的元素插入/移动到队尾
public void put(K key, T value) {
final Node cacheNode = cache.get(key);
// 无缓存历史,则new一个,按照存储是否满了决定添加方式
if (cacheNode == null) {
Node node = new Node<>(value);
if (cache.size() >= size) {
Node head = lruNode.next;
removeFromChain(head);
appendToChain(node);
} else {
appendToChain(node);
}
cache.put(key, node);
} else { // 存在缓存,则直接更新缓存,并移动到队尾
// modified value
cacheNode.value = value;
// move to last
removeFromChain(cacheNode);
appendToChain(cacheNode);
}
}
// 删除元素,被删除元素如果存在,从队列中移除
public void delete(K key) {
final Node node = cache.remove(key);
if (node != null) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
}
private void removeFromChain(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void appendToChain(Node node) {
Node last = lruNode.pre;
if (last == null) {
lruNode.next = node;
node.pre = lruNode;
} else {
last.next = node;
node.pre = last;
}
node.next = lruNode;
lruNode.pre = node;
}
// 头部元素
public T getHead() {
return lruNode.next == null ? null : lruNode.next.value;
}
/**
* 也是最后添加的或者访问过的元素
* @return last ele
*/
public T getLast() {
return lruNode.pre == null ? null : lruNode.pre.value;
}
@Override
public String toString() {
Node node = lruNode.next;
if (node == null) return "null";
StringBuilder builder = new StringBuilder();
while (node != lruNode.pre) {
builder.append(node.toString()).append(" => ");
node = node.next;
}
builder.append(node.toString());
return builder.toString();
}
/**
* node节点
* @param
*/
public static class Node {
private T value;
Node pre;
Node next;
public Node(T value) {
this.value = value;
this.pre = null;
this.next = null;
}
@Override
public String toString() {
return value == null ? "null" : value.toString();
}
}
}