LruCache的深入解析

LruCache的深入解析

大家在学习图片多级缓存的时候,肯定都会接触过LruCache这个类,本片文章对LruCache进行一个独特的,深入的分析。主要分为以下两点

  • LinkedHashMap介绍
  • LruCache关键代码分析

分析一下LinkedHashMap这个类

* API介绍:该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用 put 或 get 方法将会访问相应的条目(假定调用完成后它还存在)。
* 下边来通过代码来分析LinkedHashMap是否能够完成对最少访问到最多访问的一个顺序排序。
* 
* 先看一下LinkedHashMap的构造方法在API中的介绍

LinkedHashMap
public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder)构造一个带指定初始容量、加载因子和排序模式的空 LinkedHashMap 实例。 

参数:
initialCapacity - 初始容量
loadFactor - 加载因子
accessOrder - 排序模式 - 对于访问顺序,为 true;对于插入顺序,则为 false 
//定义一个map,设置排序方式为访问排序(看上边的构造方法介绍)
        LinkedHashMap<String, String> map = new LinkedHashMap<>(0, 0.75f, true);
        map.put("zhangsan", "12");
        map.put("lisi", "13");
        map.put("wangwu", "14");
        map.put("zhaoliu", "44");

然后我来进行遍历排序打印如下
这里写图片描述

然后再简单修改一下代码

        LinkedHashMap<String, String> map = new LinkedHashMap<>(0, 0.75f, true);
        map.put("zhangsan", "12");
        map.put("lisi", "13");
        map.put("wangwu", "14");
        map.put("zhaoliu", "44");
        map.put("wangwu", "14");

再来遍历打印如下

这里写图片描述

然后再来修改一下代码,get一下zhangsan

        LinkedHashMap<String, String> map = new LinkedHashMap<>(0, 0.75f, true);
        map.put("zhangsan", "12");
        map.put("lisi", "13");
        map.put("wangwu", "14");
        map.put("zhaoliu", "44");
        map.put("wangwu", "14");
        map.get("zhangsan");

这次遍历结果如下
这里写图片描述

经过测试,我们发现不论是添加访问还是获取访问,LinkedHashMap确实能够通过访问顺序,来进行排序。
*
* 那么下边我们来分析LruCache的源代码,这里将保留部分英文注释
*

public class LruCache<K, V> {
    /**
     * LinkedHashMap 提供特殊的构造方法来创建链接哈希映射,该哈希映射的迭代顺序就是最后访问其条目的顺序,
     * 从近期访问最少到近期访问最多的顺序(访问顺序)。 这种映射很适合构建 LRU 缓存。
     */
    private final LinkedHashMap<K, V> map;

    /**
     * 当前缓存的个数
     */
    private int size;
    /**
     * 缓存的最大个数
     */
    private int maxSize;
    /**
     * 添加到缓存中的个数
     */
    private int putCount;
    /**
     * 创建的个数
     */
    private int createCount;
    /**
     * 被移除的个数
     */
    private int evictionCount;
    /**
     * 命中个数
     */
    private int hitCount;
    /**
     * 丢失个数
     */
    private int missCount;

    /**
     *  最大缓存的大小,一般定缓存的大小
     */
    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        // 定义一个LinkedHashMa
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

    /**
     * Returns the value for {@code key} if it exists in the cache or can be
     * created by {@code #create}. If a value was returned, it is moved to the
     * head of the queue. This returns null if a value is not cached and cannot
     * be created. 根据key,获取value值,并将本次返回的value放置到对列的最上层,来表示访问的顺序(最近访问)
     */
    public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            // 从集合中获取value
            mapValue = map.get(key);
            if (mapValue != null) {
                // 如果不为空,对hitCount执行自增操作
                hitCount++;
                return mapValue;
            }
            // 如果为空,就对missCount执行自增操作
            missCount++;
        }

        /*
         * 尝试根据key去创建这样一个value
         */

        V createdValue = create(key);
        // 创建value失败,就返回一个null
        if (createdValue == null) {
            return null;
        }
        // 如果成功
        synchronized (this) {
            // 将createCount进行自增操作
            createCount++;
            // 并将创建的这个值存储到集合中去,并去获取该key之前所映射的值(如果之前没有映射,返回null)
            mapValue = map.put(key, createdValue);
            // 如果之前该key有对应的value值
            if (mapValue != null) {
                // There was a conflict so undo that last put
                // 为了避免冲突,就重新将之前的值再存进去来覆盖我们自己创造的值(这里是考虑到了create方法是在其他线程中操作)
                map.put(key, mapValue);
            } else {
                // 如果之前确实没有一个value(这回终于放心了,可以使用我们自己创造的值了,就将对缓存大小做操作)
                // 缓存的大小改变
                size += safeSizeOf(key, createdValue);
            }
        }
        // 这里没有移除,只是改变了位置
        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            // 在最后判断缓存是否超过了设定的最大值
            trimToSize(maxSize);
            return createdValue;
        }
    }

    /**
     * Caches {@code value} for {@code key}. The value is moved to the head of
     * the queue.
     * 
     * @return the previous value mapped by {@code key}.
     */
    public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        V previous;
        synchronized (this) {
            // 将添加的次数进行自增
            putCount++;
            // 改变缓存大小
            size += safeSizeOf(key, value);
            // 将本次添加,并获取之前的和key映射的value(也就是previous的value)
            previous = map.put(key, value);
            // 如果之前key是对应着value的,那就将之前value所占的一个缓存大小移掉(相当于一个车上某一个位子上原先有120斤重的人,现在上来一个150斤的,就应该+150
            // -120if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }
        // 看一下当前大小是否超过总的大小
        trimToSize(maxSize);
        return previous;
    }

    /**
     * Remove the eldest entries until the total of remaining entries is at or
     * below the requested size. 如果我们定义的size>maxSize 就移除一个最不常用的数据(或者是
     * 最老的数据),直到我们定义的size<maxSize
     * 
     * @param maxSize
     *            the maximum size of the cache before returning. May be -1 to
     *            evict even 0-sized elements.
     */
    public void trimToSize(int maxSize) {
        /**
         * 这里是一个无限循环的操作,如果我们的size一直大于maxSize,就一直执行这个方法 比如车的载重是400斤,之前有四个人,分别是
         * 100斤,99斤,98斤,97斤,现在上来一个人是300斤,那就得执行移除三个人的方法,才能满足不超重
         */
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }
                // 如果本来没超过,就中断循环
                if (size <= maxSize || map.isEmpty()) {
                    break;
                }
                // 移除最少使用的缓存
//              Set<Entry<K,V>> entrySet = map.entrySet();
                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                // 将当前的size减掉所移除的size
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

    /**
     * Removes the entry for {@code key} if it exists.
     * 用户手动移除
     * 
     * @return the previous value mapped by {@code key}.
     */
    public final V remove(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V previous;
        synchronized (this) {
            previous = map.remove(key);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, null);
        }

        return previous;
    }

    /**
     *            //这里用户可以重写它,实现数据和内存回收操作,默认是没有任何实现
     */
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {
    }
    /**
     * Returns the size of the entry for {@code key} and {@code value} in
     * user-defined units. The default implementation returns 1 so that size is
     * the number of entries and max size is the maximum number of entries.
     * //这里跟我们实例化 LruCache 的 maxSize 要对应起来,如果 maxSize在初始定义的时候是定义的个数 这里就是 return
     * 1,如果是内存的大小,如果5M,这是应该是每个缓存 value 的 size 大小,如果是 Bitmap,这应该是
     * bitmap.getByteCount(); 所以这个方法一般会由用户进行重写
     * 
     */
    protected int sizeOf(K key, V value) {
        return 1;
    }

    /**
     * Clear the cache
     * 清除所有缓存
     */
    public final void evictAll() {
        trimToSize(-1); // -1 will evict 0-sized elements
    }



    /**
     * Returns a copy of the current contents of the cache, ordered from least
     * recently accessed to most recently accessed. 返回缓存的一个备份,从最不常用到最常用进行排序
     */
    public synchronized final Map<K, V> snapshot() {
        return new LinkedHashMap<K, V>(map);
    }

以上代码是LruCache的关键代码,已在代码中给出注释,大家在学习的时候,可以自己导入v4包的源码来研究这个类的源码。
本篇文章是对LruCache的一个源码解析,如想对图片多级缓存(包括DiskLruCache及缓存原理)了解更多,请将关注本人。

需求响应动态冰蓄冷系统与需求响应策略的优化研究(Matlab代码实现)内容概要:本文围绕“需求响应动态冰蓄冷系统与需求响应策略的优化研究”展开,基于Matlab代码实现,重点探讨了冰蓄冷系统在电力需求响应背景下的动态建模与优化调度策略。研究结合实际电力负荷与电价信号,构建系统能耗模型,利用优化算法对冰蓄冷系统的运行策略进行求解,旨在降低用电成本、平衡电网负荷,并提升能源利用效率。文中还提及该研究为博士论文复现,涉及系统建模、优化算法应用与仿真验证等关键技术环节,配套提供了完整的Matlab代码资源。; 适合人群:具备一定电力系统、能源管理或优化算法基础,从事科研或工程应用的研究生、高校教师及企业研发人员,尤其适合开展需求响应、综合能源系统优化等相关课题研究的人员。; 使用场景及目标:①复现博士论文中的冰蓄冷系统需求响应优化模型;②学习Matlab在能源系统建模与优化中的具体实现方法;③掌握需求响应策略的设计思路与仿真验证流程,服务于科研项目、论文写作或实际工程方案设计。; 阅读建议:建议结合提供的Matlab代码逐模块分析,重点关注系统建模逻辑与优化算法的实现细节,按文档目录顺序系统学习,并尝试调整参数进行仿真对比,以深入理解不同需求响应策略的效果差异。
综合能源系统零碳优化调度研究(Matlab代码实现)内容概要:本文围绕“综合能源系统零碳优化调度研究”,提供了基于Matlab代码实现的完整解决方案,重点探讨了在高比例可再生能源接入背景下,如何通过优化调度实现零碳排放目标。文中涉及多种先进优化算法(如改进遗传算法、粒子群优化、ADMM等)在综合能源系统中的应用,涵盖风光场景生成、储能配置、需求响应、微电网协同调度等多个关键技术环节,并结合具体案例(如压缩空气储能、光热电站、P2G技术等)进行建模与仿真分析,展示了从问题建模、算法设计到结果验证的全流程实现过程。; 适合人群:具备一定电力系统、能源系统或优化理论基础,熟悉Matlab/Simulink编程,从事新能源、智能电网、综合能源系统等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①开展综合能源系统低碳/零碳调度的科研建模与算法开发;②复现高水平期刊(如SCI/EI)论文中的优化模型与仿真结果;③学习如何将智能优化算法(如遗传算法、灰狼优化、ADMM等)应用于实际能源系统调度问题;④掌握Matlab在能源系统仿真与优化中的典型应用方法。; 阅读建议:建议结合文中提供的Matlab代码与网盘资源,边学习理论模型边动手调试程序,重点关注不同优化算法在调度模型中的实现细节与参数设置,同时可扩展应用于自身研究课题中,提升科研效率与模型精度。
本系统采用Java作为核心编程语言,基于Spring Boot框架构建,运行环境配置为JDK 1.8与Tomcat 7应用服务器。数据存储选用MySQL 5.7数据库,并借助Navicat 11进行数据库管理操作。开发工具可在Eclipse、MyEclipse或IntelliJ IDEA中任选,项目依赖管理通过Maven 3.3.9完成。 该简历管理平台在架构设计阶段,着重强化了代码结构的清晰度与可维护性,同时兼顾系统的实用价值与扩展灵活性。整体设计遵循通用化原则,确保后期维护简便,用户界面力求直观简洁。 系统通过标签分类机制实现功能模块化管理,主要包含以下三个角色维度: 管理员端涵盖综合管理功能:包括控制面板、用户账户管理、简历模板维护、模板分类设置、招聘会组织、报名信息处理、上传简历审核、求职社区管理、收藏夹维护及系统参数配置。 普通用户端提供个性化功能:涉及个人中心、招聘活动报名、简历文件上传及个人收藏记录管理。 公共访问端集成信息展示与交互模块:主要展示首页内容、简历模板库、招聘会资讯、简历上传入口、求职讨论区、系统公告栏,并提供个人中心入口、后台管理通道与在线客户服务。 该系统旨在通过结构化功能设计,提升简历相关数据的管理效率与组织化程度。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值