首页 > 编程语言 >LRU缓存与LinkedHashMap源码

LRU缓存与LinkedHashMap源码

时间:2023-06-03 19:33:44浏览次数:50  
标签:缓存 get int 源码 LRU key put LinkedHashMap

今天再刷LeetCode时,遇到了第146题LRU缓存。题目如下:

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

如果大家有阅读过LinkedHashMap源码,就会明白,这题的解法跟LinkedHashMap源码一模一样。

大家应该经常使用HashMap,而LinkedHashMap 刚好就比 HashMap 多这一个功能就是有序。并且,LinkedHashMap的有序可以按两种顺序排列,一种是按照插入的顺序,一种是按照 读取 的顺序。显然,这题便是LinkedHashMap中按照读取的顺序进行顺序排列。而其内部是靠 建立一个双向链表 来维护这个顺序的,在每次插入、删除后,都会调用一个函数来进行 双向链表的维护

其内部就是靠如下三个回调方法来维护这个双向链表:

void afterNodeAccess(Node<K,V> p) { }
其作用就是在访问元素之后,将该元素放到双向链表的尾巴处(所以这个函数只有在按照读取的顺序的时候才会执行),之所以提这个,是建议大家去看看,如何优美的实现在双向链表中将指定元素放入链尾!
void afterNodeRemoval(Node<K,V> p) { }
其作用就是在删除元素之后,将元素从双向链表中删除,还是非常建议大家去看看这个函数的,很优美的方式在双向链表中删除节点!
void afterNodeInsertion(boolean evict) { }
这个才是我们题目中会用到的,在插入新元素之后,需要回调函数判断是否需要移除一直不用的某些元素!

以下是我对于afterNodeAccess、afterNodeRemoval、afterNodeInsertion的个人理解:

afterNodeAccess:

afterNodeRemoval:

afterNodeInsertion:


/**
 * 插入新节点才会触发该方法,因为只有插入新节点才需要内存
 * 根据 HashMap 的 putVal 方法, evict 一直是 true
 * removeEldestEntry 方法表示移除规则, 在 LinkedHashMap 里一直返回 false
 * 所以在 LinkedHashMap 里这个方法相当于什么都不做
 */
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是默认返回false的,我们可以继承LinkedHashMap然后复写该方法即可
// 例如 LeetCode 第 146 题就是采用该种方法,直接 return size() > capacity;
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

通过上述代码,我们就已经知道了只要复写 removeEldestEntry() 即可,而条件就是 map 的大小不超过 给定的容量,超过了就得使用 LRU 了!然后根据题目给定的语句构造和调用:
其主要是两个构造方法,一个是继承 HashMap ,一个是可以选择 accessOrder 的值(默认 false,代表按照插入顺序排序)来确定是按插入顺序还是读取顺序排序。

//调用父类HashMap的构造方法。
public LinkedHashMap() {
    super();
    accessOrder = false;
}
// 这里的 accessOrder 默认是为false,如果要按读取顺序排序需要将其设为 true
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

最后该题解法如下:

class LRUCache extends LinkedHashMap<Integer,Integer> {
    int capacity=10;
    public LRUCache(int capacity) {
        super(capacity,0.75F,true);
        this.capacity=capacity;
    }
    
    public int get(int key) {
      return   this.getOrDefault(key,-1);
    }
    
    public void put(int key, int value) {
            super.put(key,value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size()>capacity;
    }
}

完美解决!

参考链接:
https://leetcode.cn/problems/lru-cache/solution/yuan-yu-linkedhashmapyuan-ma-by-jeromememory/

标签:缓存,get,int,源码,LRU,key,put,LinkedHashMap
From: https://www.cnblogs.com/BestJaxXu/p/17454440.html

相关文章

  • (五)Spring源码解析:ApplicationContext源码解析
    一、概述1.1>整体概览在前面的内容中,我们针对BeanFactory进行了深度的分析。那么,下面我们将针对BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与BeanFactory的功能相似,都是用于向IOC中加载Bean的。由于ApplicationConext的功能是大于BeanFactory的......
  • 大件货运系统源码,技术架构:spring boot、mybatis、redis、vue、element-ui
    网络货运平台源码网络货运平台的功能网络货运是指利用互联网平台,通过物流配送的方式进行商品销售和物流运输的一种新型商业模式。这种模式将传统的货运模式与互联网技术相结合,通过网络平台进行交易、物流配送和结算等一系列流程,从而实现货物的快速、高效、便捷地运输。技术架构:spr......
  • JAVA的springboot+vue医疗预约服务管理信息系统,医院预约管理系统,附源码+数据库+论文+P
    1、项目介绍会员制医疗预约服务管理信息系统是针对会员制医疗预约服务管理方面必不可少的一个部分。在会员制医疗预约服务管理的整个过程中,会员制医疗预约服务管理系统担负着最重要的角色。为满足如今日益复杂的管理需求,各类的管理系统也在不断改进。本课题所设计的是会员制医疗......
  • LOOK!两步控制直播APP源码平台的稳定
    随着网络时代的发展,直播慢慢深入到我们日常生活中来,直播不仅仅成为人们休闲娱乐的方式,他也变成了人们工作、学习等一些方式,这就使直播APP源码平台的人数的巨大,这也增加了运营商的烦恼,当直播APP源码平台的直播间中观看用户到达一定限度时,如何能保证直播的稳定进行?当然,这也就是我们今......
  • LOOK!两步控制直播APP源码平台的稳定
     随着网络时代的发展,直播慢慢深入到我们日常生活中来,直播不仅仅成为人们休闲娱乐的方式,他也变成了人们工作、学习等一些方式,这就使直播APP源码平台的人数的巨大,这也增加了运营商的烦恼,当直播APP源码平台的直播间中观看用户到达一定限度时,如何能保证直播的稳定进行?当然,这也就是我......
  • 系统ubuntu20.04-ROS2源码安装humble
    系统要求HumbleHawksbill目前基于Debian的目标平台是Tier1:UbuntuLinux-Jammy(22.04)64-bitTier3:UbuntuLinux-Focal(20.04)64-bitDebianLinux-Bullseye(11)64-bit其他具有不同支持级别的Linux平台包括:ArchLinux,seealternateinstructionsFedoraLinux,s......
  • 基于JAVA的springboot篮球论坛系统,附源码+数据库+论文+PPT
    1、项目介绍考虑到实际生活中在篮球论坛方面的需要以及对该系统认真的分析,将系统权限按管理员和用户这两类涉及用户划分。(a)管理员;管理员使用本系统涉到的功能主要有:首页、个人中心、用户管理、篮球论坛、系统管理等功能。管理员用例图如图3-1所示。(b)用户;用户使用本系统......
  • do_page_fault源码阅读
    前言参考《Linux内核源码情景分析》对缺页异常的处理过程,但是书中的kernelversion版本较老,因此本文基于kernelversion4.19.20源码,参考oldversion的内核源码剖析,再次进行了阅读。缺页异常的产生原因缺页异常就在通过虚拟地址去访问物理内存的过程中出现失败时抛出的异常,访问......
  • 还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】
    前几天,远在北京的小伙伴在群里抛出了“MapStruct”的概念。对于只闻其名,未见其人的我来说,决定对其研究一番。本文我们就从MapStruct的概念出发,通过具体的代码示例来研究它的使用情况,最后与“市面上”的其它工具来做个对比!官方介绍首先我们打开MapStruct的官网地址,映入眼帘的就......
  • 独立商户商城全套方案带源码
    前两天分享了一个基于微信生态的多租户商城[分享一个基于微信生态的多租户商城]这个部署起来比较麻烦,首先需要一个认证的微信开发平台账号和一个认证的微信公众号账号。今天分享另外一个商城,这个商城跟微信生态没有绑定这么紧密,但是功能相对还是满满的。0x01:后台端服务仓库地址......