LRU缓存实现
LinkedHashMap源码剖析
继承LinkedHashMap
可以通过继承 LinkedHashMap 实现 LRU 缓存结构。
代码如下:
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int MAX_ENTRIES;
public LRUCache (int maxEntries){
// /**
// * Constructs an empty <tt>LinkedHashMap</tt> instance with the
// * specified initial capacity, load factor and ordering mode.
// *
// * @param initialCapacity the initial capacity
// * @param loadFactor the load factor
// * @param accessOrder the ordering mode - <tt>true</tt> for
// * access-order, <tt>false</tt> for insertion-order
// * @throws IllegalArgumentException if the initial capacity is negative
// * or the load factor is nonpositive
// */
// public LinkedHashMap(int initialCapacity,
// float loadFactor,
// boolean accessOrder) {
// super(initialCapacity, loadFactor);
// this.accessOrder = accessOrder;
// }
super(maxEntries, 0.5f, true);
MAX_ENTRIES = maxEntries;
}
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
}
自己的实现 (时间复杂度比较高,运行时间可能超时)
public class MyLRU<K, V> {
private final int MAX_ENTRIES;
private HashMap<K, V> map;
private LinkedList<K> lruList;
public MyLRU(int maxEntries){
MAX_ENTRIES = maxEntries;
map = new HashMap<>(maxEntries);
lruList = new LinkedList<>();
}
public void set(K key, V value){
map.put(key, value);
afterAccess(key);
afterInsert(key);
}
public V get(K key){
V res = map.get(key);
afterAccess(key);
return res;
}
/**
* 缓存溢出,去掉最近最少存取的元素,即链表头元素
* @param key
*/
private void afterInsert(K key) {
// System.out.println("lruList.size: " + lruList.size());
if(lruList.size() > MAX_ENTRIES) {
K rmKey = lruList.removeFirst();
// System.out.println("rmKey: " + rmKey);
map.remove(rmKey);
}
}
/**
* 存取操作都将使对应的 key 移动到链表尾部
* @param key
*/
private void afterAccess(K key) {
lruList.remove(key);
lruList.addLast(key);
}
}
改进的自定义实现(需要自定义结点和链表结构) (运行时间可以满足要求)
/**
* @author 49367
* @date 2021/3/29 15:23
*/
public class MyLRU<K, V> {
private final int MAX_SIZE;
private HashMap<K, LRUNode> cache;
class LRUNode{
K key;
V val;
LRUNode pre;
LRUNode next;
LRUNode(K key, V value){
this.key = key;
this.val = value;
}
}
/**
* 链表虚的头结点和尾结点,不存储任何数据
* 当head.next = tail, tail.pre = head时,链表为空
* 之所以设计成虚结点,是为了方便添加和删除操作
*/
LRUNode head, tail;
public MyLRU(int maxSize){
MAX_SIZE = maxSize;
cache = new HashMap<>(maxSize);
head = new LRUNode(null, null);
tail = new LRUNode(null, null);
head.next = tail;
head.pre = null;
tail.next = null;
tail.pre = head;
}
/**
* 添加或者更新操作,如果是添加操作,需要进行缓存溢出判断,
* 如果溢出,删去最近最少使用的数据。
*
* 将key对应的结点移动或者添加到表头
* @param key
* @param value
*/
public void set(K key, V value){
LRUNode node = cache.get(key);
if (node == null) {
// 如果 key 不存在,创建一个新的节点
LRUNode newNode = new LRUNode(key, value);
// 添加进哈希表
cache.put(key, newNode);
// 添加至双向链表的头部
headAddNode(newNode);
if (cache.size() > MAX_SIZE) {
// 如果超出容量,删除双向链表的尾部节点
LRUNode rmNode = removeTailNode();
if(rmNode != null)
// 删除哈希表中对应的项
cache.remove(rmNode.key);
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
node.val = value;
moveToHead(node);
}
}
/**
* 获取操作,将key对应的结点移动至链表头部
* @param key
* @return
*/
public V get(K key){
if(!cache.containsKey(key))
return null;
LRUNode node = cache.get(key);
moveToHead(node);
return node.val;
}
private void moveToHead(LRUNode node) {
removeNode(node);
headAddNode(node);
}
private void removeNode(LRUNode node){
if(node != null) {
node.pre.next = node.next;
node.next.pre = node.pre;
node.pre = null;
node.next = null;
}
}
private LRUNode removeTailNode(){
if(tail.pre == head)
return null;
LRUNode rmNode = tail.pre;
removeNode(rmNode);
return rmNode;
}
private void headAddNode(LRUNode node){
LRUNode next = head.next;
head.next = node;
node.pre = head;
node.next = next;
next.pre = node;
}
}