利用LRU(Least Recently Used)封装内存缓存LruCache,Kotlin版本。

9 篇文章 0 订阅

LRU简介

LRU(Least Recently Used),即最近最少使用,是一种常用的页面置换算法,当需要存入的内容大于临界值的时候,选择最近最少使用的进行淘汰。

如果使用简单的数据结构描述的话,当前有一个栈,但是与栈不同的是,可以随时访问栈中的元素,并且访问过后将当前访问元素置于栈顶。

使用Kotlin写一个LruCache

实现原理

如果写一个缓存的话,一般都是keyvalue类型的,那么自然就会想到HashMap,但是HashMap是无序的。所以我们考虑使用LinkedHashMap,它内部在每一个Node节点上新增了before和after做成双向链表,用于记录指向顺序,可以表示插入顺序/访问顺序

我们这里就是用到其的访问顺序。这样每次我们从LinkedHashMap里面获取值的时候,该值就会跑到链表尾部,插入一个值也是在链表尾部。这样就完美实现类最近最少访问。接下来只需要在每次向map存值的时候,将大于我们所设置好的最大size的从栈头部便利去除即可。

代码

/**
 * LRU(Least Recently Used) 缓存算法,最近最少被使用,则被移除缓存
 *
 * @author pumpkin
 */
class LruCache<K, V>(maxSize: Int) {

    /**
     * 当前储存的size
     */
    private var currentSize = 0

    /**
     * 允许保存的最大的size,可以进行重置,不允许小于0
     */
    var maxSize = maxSize
        set(value) {
            if (value < 0) {
                throw IllegalStateException("maxSize 不允许小于0!")
            }
            synchronized(this) {
                field = value
                //重新计算,删除最旧的
                trimToSize()
            }
        }
        get() {
            synchronized(this) {
                return field
            }
        }

    /**
     * 缓存的容量为16,loadFactor为0.75F 与HashMap保持一致
     * 设置accessOrder为true,表示使用访问顺序,即一旦get某个值,该值就会被指向在链表的最后。默认为false,表示的是插入顺序。
     */
    private val cache = LinkedHashMap<K, V>(1 shl 4, 0.75F, true)

    var customSizeOf: ((key: K, value: V) -> Int)? = null

    /**
     * 删除最旧item,直到剩余的item的size小于maxSize
     */
    private fun trimToSize() {
        synchronized(this) {
            if (currentSize < 0 || (cache.isEmpty() && currentSize != 0)) {
                throw IllegalStateException("缓存size记录错误")
            }
            while (currentSize > maxSize) {
                val iterator = cache.entries.iterator()
                if (iterator.hasNext()) {
                    val (key, value) = iterator.next()
                    currentSize -= sizeOf(key, value)
                    iterator.remove()
                }
            }
        }
    }

    /**
     * null 检查
     */
    private fun <T> nullCheck(message: String, any: T?): T {
        if (any == null) {
            throw NullPointerException(message)
        }
        return any
    }

    /**
     * 获取值get
     */
    operator fun get(key: K?): V? {
        nullCheck("key 不允许等于 null", key)
        //利用linkedHashMap的特性,默认会将当前访问的链表指向到最后。
        return cache[key]
    }

    /**
     * 存值put
     * @return 若当前key之前存在值则返回,否则返回null
     */
    operator fun set(key: K?, value: V?): V? {
        nullCheck("key 不允许等于 null", key)
        nullCheck("value 不允许等于 null", value)

        val oldValue = synchronized(this) {
            val size = sizeOf(key!!, value!!)

            //如果存入size 大于maxSize 直接return
            if (size > maxSize) {
                return null
            }

            currentSize += size
            cache.put(key, value)?.also { oldValue ->
                currentSize -= sizeOf(key, oldValue)
            }
        }

        //位置移动
        trimToSize()

        return oldValue
    }

    /**
     * 默认每个item的size默认返回1,可以利用customSizeOf修改
     */
    private fun sizeOf(key: K, value: V): Int {
        val snapSizeOf = customSizeOf
        if (snapSizeOf != null) {
            return snapSizeOf(key, value)
        }
        return 1
    }

    /**
     * 清除所有的缓存
     */
    fun clearAll() {
        maxSize = 0
    }
}

代码GIT地址:➡️➡️➡️➡️➡️➡️➡️➡️➡️➡️➡️➡️里面有TestActivity包含具体使用方法。

创作不易,如有帮助一键三连咯🙆‍♀️。欢迎技术探讨噢!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pumpkin的玄学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值