LRU (Least recently used:最近最少使用)算法
先上代码
public class LRUCache extends LinkedHashMap<Object,Object> {
private static final long serialVersionUID = 1L;
// 容纳的最大数量,是我们缓存的最大数量,也是触发回收的临界点
Integer cacheSize;
public LRUCache(Integer cacheSize) {
super(cacheSize, 0.75f, true);
this.cacheSize = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > cacheSize;
}
}
测试一下
public static void main(String[] args) {
LRUCache lruCache = new LRUCache(3);
lruCache.put("1", "1");
lruCache.put("2", "2");
lruCache.put("3", "3");
lruCache.put("4", "4");
System.out.println(lruCache);
}
代码很简单,我这边是基于1.8 的版本,可以看到我的最大容量设置的是3,当塞入第四个参数的时候,第一个值被删除了,而LinkedHashMap 是按照插入顺序存储的,我们可以猜测会优先移除头数据,看看代码是不是这样
代码分析
首先我们重写了构造方法,父类的构造方法最重要的就是设置accessOrder 属性为 true
然后重写removeEldestEntry()方法, 这个在插入元素后告诉程序改执行删除操作了,
首先我们看下put()方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
.....
// 这个方法判断是否需要删除
afterNodeInsertion(evict);
return null;
}
.....
// 最后到这个afterNodeInsertion方法中,我们可以看到备注上写着是不是需要把老的移除
// 重写的removeEldestEntry方法就在这个了
// 我们可以看到,这边移除的是头元素,猜测验证了
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
再看下get()方法,那么按照道理,它应该是将访问的元素放到了最后边
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
// 这个accessOrder就是我们的构造防范传入的第三个参数,会触发访问元素移动
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
// 这个方法,会将刚才访问的元素,移动到尾部
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
removeEldestEntry()方法是判断是否需要启动回收的方法,在一些业务中可以还得进行资源回收,像mysql 的连接jar包中也有一个LRUCache,其中一个是存储ServerPreparedStatement对象,删除的时候需要关闭连接