LinkedHashMap实现LRU – 附重点源码解析
最近接触LRU(Least Recently Used),即最近最少使用,也称淘汰算法,在JDK中LinkedHashMap有相关实现,下面针对LRU及LinkedHashMap的LRU实现进行详细讲解
1. 为什么使用LRU
有些数据需要缓存在内存中,以便高效查询。但是当缓存的数据量很大,并且某一时间段只有某小部分缓存数据被频繁使用(称之为热点数据),而其余缓存数据暂时没有访问,这时就需要LRU策略对热点数据进行保留,对非热点数据进行及时下线,保证缓存空间健康。
应用场景: 商城分时段商品秒杀
2. LRU的LinkedHashMap实现
创立LRULinkedHashMap继承LinkedHashMap并重写removeEldestEntry方法,该方法返回的boolean代表能否删除最早使用/存放的Entry。
- LRULinkedHashMap
public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> { private int threshold; public LRULinkedHashMap(int threshold) { super(16, 0.75f, true); this.threshold = threshold; } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > threshold; }}- 使用
- get操作,会将节点移动到队尾。
- put操作,假如key不存在,则节点放置队尾,假如节点存在,会将节点移动到队尾。
public static void main(String[] args) { LRULinkedHashMap<String, String> lruLinkedHashMap = new LRULinkedHashMap<>(5); lruLinkedHashMap.put("1", null); lruLinkedHashMap.put("2", null); lruLinkedHashMap.put("3", null); lruLinkedHashMap.put("4", null); lruLinkedHashMap.put("5", null); lruLinkedHashMap.put("6", null); lruLinkedHashMap.put("7", null); System.out.println(lruLinkedHashMap); lruLinkedHashMap.get("4"); System.out.println(lruLinkedHashMap); lruLinkedHashMap.put("5", null); System.out.println(lruLinkedHashMap); out => {3=null, 4=null, 5=null, 6=null, 7=null} out => {3=null, 5=null, 6=null, 7=null, 4=null} out => {3=null, 6=null, 7=null, 4=null, 5=null}}3. LinkedHashMap实现Map有序 – 源码分析
LinkedHashMap继承自HashMap,HashMap采用数组加链表的结构存储数据,存储节点为HashMap.Node,分别存放hash值,key,value,以及指向下一个Node节点的next指针,链表结构是单项链表,HashMap并没有维护有序性。
HashMap
image.png
LinkedHashMap继承了HashMap,也是采用了数据加链表的结构,不同的是LinkedHashMap的存储节点(Entry)继承自HashMap.Node,多维护了before和after指针,用来指向上一个和下一个节点,实现双向链表。这个双向链表即可以实现Map有序性(access-order:访问顺序/insertion-order插入顺序,默认是insertion-order)。
LinkedHashMap
image.png
重点代码
- HashMap.Node
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; ...}- LinkedHashMap.Entry
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; ...}- HashMap – newNode
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) { return new Node<>(hash, key, value, next);}- LinkedHashMap – newNode
// 重写了HashMap - newNodeNode<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<>(hash, key, value, e); linkNodeLast(p); // 增加节点到队尾 return p;}- HashMap – forEach
先遍历数组,再顺序遍历链表
@Overridepublic void forEach(BiConsumer<? super K, ? super V> action) { Node<K,V>[] tab; if (action == null) throw new NullPointerException(); if (size > 0 && (tab = table) != null) { int mc = modCount; for (Node<K,V> e : tab) { // 先遍历数据 for (; e != null; e = e.next) //再书序遍历链表 action.accept(e.key, e.value); } if (modCount != mc) throw new ConcurrentModificationException(); }}- LinkedHashMap – forEach
直接遍历双向链表,实现有序性。
// Map overridespublic void forEach(BiConsumer<? super K, ? super V> action) { if (action == null) throw new NullPointerException(); int mc = modCount; for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) // 直接遍历 action.accept(e.key, e.value); if (modCount != mc) throw new ConcurrentModificationException();}4. LinkedHashMap实现LRU – 源码分析
下面是设置LinkedHashMap为访问顺序时的示用意。
image.png
- LinkedHashMap – 构造器
accessOrder默认为false,即维护插入顺序,设置为true时,维护访问顺序。
* @param accessOrder the ordering mode - {@code true} for access-order, {@code false} for insertion-orderpublic LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder;}public LinkedHashMap() { super(); accessOrder = false; // 默认为插入顺序}- LinkedHashMap – get
当设置为访问顺序时,在get/put的时候调用afterNodeAccess方法,将节点移动到队尾。
public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) afterNodeAccess(e);// 将节点移动到队尾 return e.value;}- LinkedHashMap – put – 升级值
下面是HashMap的put操作,调用LinkedHashMap 的afterNodeAccess方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { .... if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); // 将节点移动到队尾 return oldValue; } ....}- LinkedHashMap – put – 插入新值
插入新值, 调用linkNodeLast增加节点到队尾
// 重写了HashMap - newNodeNode<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<>(hash, key, value, e); linkNodeLast(p); // 增加节点到队尾 return p;}相关参考
- LeetCode – LRU Cache (Java)
- 玩转Redis:8 种数据淘汰策略及近似LRU、LFU原理!
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » LinkedHashMap实现LRU – 附重点源码解析
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » LinkedHashMap实现LRU – 附重点源码解析