首页 技术 正文
技术 2022年11月21日
0 收藏 817 点赞 5,063 浏览 2359 个字

date: 2020-07-09 13:52:00

updated: 2020-07-21 17:40:00

LinkedHashMap 实现LRU缓存

参考

LinkedHashMap是HashMap的子类,但是内部还有一个双向链表维护键值对的顺序,每个键值对既位于哈希表中,也位于双向链表中。LinkedHashMap支持两种顺序插入顺序 、 访问顺序

插入顺序:先添加的在前面,后添加的在后面。修改操作不影响顺序

访问顺序:所谓访问指的是get/put操作,对一个键执行get/put操作后,其对应的键值对会移动到链表末尾,所以最末尾的是最近访问的,最开始的是最久没有被访问的,这就是访问顺序。

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 其中参数accessOrder就是用来指定是否按访问顺序,如果为true,就是访问顺序。

LRU(Least Recently Used): 最近最少使用

public class LRUCache<K, V> extends LinkedHashMap<K, V> {    private int maxEntries;    public LRUCache(int maxEntries) {
super(16, 0.75f, true);
this.maxEntries = maxEntries;
} @Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
}

在LinkedHashMap添加元素后,会调用removeEldestEntry防范,传递的参数时最久没有被访问的键值对,如果方法返回true,这个最久的键值对就会被删除。LinkedHashMap中的实现总返回false,该子类重写后即可实现对容量的控制。

LRUCache<String,Object> cache = new LRUCache<>(3);
cache.put("a","abstract");
cache.put("b","basic");
cache.put("c","call");
cache.get("a");
cache.put("d","滴滴滴");
System.out.println(cache); // 输出为:{c=call, a=abstract, d=滴滴滴}

相比HashMap,LinkedHashMap还实现了三个方法,当且仅当 accessOrder=True 时会被调用到

void afterNodeAccess(Node<K,V> p) { }  //访问节点之后调用的方法
void afterNodeInsertion(boolean evict) { } //插入节点之后调用的方法
void afterNodeRemoval(Node<K,V> p) { } //删除节点之后调用的方法
在get()方法中调用afterNodeAccess()方法,具体作用是:在对节点进行访问之后,会更新链表,将节点移动到链表的尾部,表示最近被访问过。
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;
}
}
LinkedHashMap的put()是调用的父类HashMap的put()方法
这个方法是在HashMap中的put()方法中被调用,在LinkedHashMap中被实现,具体作用是:在插入新节点后,因为缓存不够,需要删除最近最少使用的节点。要成功调用这个方法,还需要用户覆写 removeEldestEntry(first) 方法
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);
}
}
当LinkedHashMap成功调用removeNode()方法,删除节点之后,会调用本方法
具体作用是:在removeNode方法中,只是删除了HashMap中的节点,并没有在链表中删除。所以在removeNode中,回调了这个方法,将该节点从链表中删除(这里是删除的头结点,因为头结点是最早进入或者最近最久未使用的)。
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,020
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,513
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,359
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,142
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,772
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,850