LruCache.kt 5.39 KB
Newer Older
konghaorui committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192

package com.ydl.ydlnet.cache.utils

import java.util.*
/**
 * Created by haorui on 2019-08-22 .
 * Des: LRU缓存策略,即最近最少使用
 */
class LruCache<K, V>
/**
 * Constructor for LruCache.
 *
 * @param size 这个缓存的最大 size,这个 size 所使用的单位必须和 [.getItemSize] 所使用的单位一致.
 */
    (private val initialMaxSize: Int) {
    private val cache = LinkedHashMap<K, V>(100, 0.75f, true)
    private var maxSize: Int = 0
    private var currentSize = 0

    init {
        this.maxSize = initialMaxSize
    }

    /**
     * 设置一个系数应用于当时构造函数中所传入的 size, 从而得到一个新的 [.maxSize]
     * 并会立即调用 [.evict] 开始清除满足条件的条目
     *
     * @param multiplier 系数
     */
    @Synchronized
    fun setSizeMultiplier(multiplier: Float) {
        if (multiplier < 0) {
            throw IllegalArgumentException("Multiplier must be >= 0")
        }
        maxSize = Math.round(initialMaxSize * multiplier)
        evict()
    }

    /**
     * 返回每个 `item` 所占用的 size,默认为1,这个 size 的单位必须和构造函数所传入的 size 一致
     * 子类可以重写这个方法以适应不同的单位,比如说 bytes
     *
     * @param item 每个 `item` 所占用的 size
     * @return 单个 item 的 `size`
     */
    protected fun getItemSize(item: V?): Int {
        return 1
    }

    /**
     * 当缓存中有被驱逐的条目时,会回调此方法,默认空实现,子类可以重写这个方法
     *
     * @param key   被驱逐条目的 `key`
     * @param value 被驱逐条目的 `value`
     */
    protected fun onItemEvicted(key: K, value: V?) {
        // optional override
    }

    /**
     * 返回当前缓存所能允许的最大 size
     *
     * @return `maxSize`
     */
    @Synchronized
    fun getMaxSize(): Int {
        return maxSize
    }

    /**
     * 返回当前缓存已占用的总 size
     *
     * @return `size`
     */
    @Synchronized
    fun size(): Int {
        return currentSize
    }

    /**
     * 如果这个 `key` 在缓存中有对应的 `value` 并且不为 `null`,则返回 true
     *
     * @param key 用来映射的 `key`
     * @return `true` 为在容器中含有这个 `key`, 否则为 `false`
     */
    @Synchronized
    fun containsKey(key: K): Boolean {
        return cache.containsKey(key)
    }

    /**
     * 返回当前缓存中含有的所有 `key`
     *
     * @return `keySet`
     */
    @Synchronized
    fun keySet(): MutableSet<K> {
        return cache.keys
    }

    /**
     * 返回这个 `key` 在缓存中对应的 `value`, 如果返回 `null` 说明这个 `key` 没有对应的 `value`
     *
     * @param key 用来映射的 `key`
     * @return `value`
     */
    @Synchronized
    fun get(key: K): V? {
        return cache[key]
    }

    /**
     * 将 `key` 和 `value` 以条目的形式加入缓存,如果这个 `key` 在缓存中已经有对应的 `value`
     * 则此 `value` 被新的 `value` 替换并返回,如果为 `null` 说明是一个新条目
     *
     *
     * 如果 [.getItemSize] 返回的 size 大于或等于缓存所能允许的最大 size, 则不能向缓存中添加此条目
     * 此时会回调 [.onItemEvicted] 通知此方法当前被驱逐的条目
     *
     * @param key   通过这个 `key` 添加条目
     * @param value 需要添加的 `value`
     * @return 如果这个 `key` 在容器中已经储存有 `value`, 则返回之前的 `value` 否则返回 `null`
     */
    @Synchronized
    fun put(key: K, value: V): V? {
        val itemSize = getItemSize(value)
        if (itemSize >= maxSize) {
            onItemEvicted(key, value)
            return null
        }

        val result = cache.put(key, value)
        if (value != null) {
            currentSize += getItemSize(value)
        }
        if (result != null) {
            currentSize -= getItemSize(result)
        }
        evict()

        return result
    }

    /**
     * 移除缓存中这个 `key` 所对应的条目,并返回所移除条目的 `value`
     * 如果返回为 `null` 则有可能时因为这个 `key` 对应的 `value` 为 `null` 或条目不存在
     *
     * @param key 使用这个 `key` 移除对应的条目
     * @return 如果这个 `key` 在容器中已经储存有 `value` 并且删除成功则返回删除的 `value`, 否则返回 `null`
     */
    @Synchronized
    fun remove(key: K): V? {
        val value = cache.remove(key)
        if (value != null) {
            currentSize -= getItemSize(value)
        }
        return value
    }

    /**
     * 清除缓存中所有的内容
     */
    fun clear() {
        trimToSize(0)
    }

    /**
     * 当指定的 size 小于当前缓存已占用的总 size 时,会开始清除缓存中最近最少使用的条目
     *
     * @param size `size`
     */
    @Synchronized
    protected fun trimToSize(size: Int) {
        var last: Map.Entry<K, V>
        while (currentSize > size) {
            last = cache.entries.iterator().next()
            val toRemove = last.value
            currentSize -= getItemSize(toRemove)
            val key = last.key
            cache.remove(key)
            onItemEvicted(key, toRemove)
        }
    }

    /**
     * 当缓存中已占用的总 size 大于所能允许的最大 size ,会使用  [.trimToSize] 开始清除满足条件的条目
     */
    private fun evict() {
        trimToSize(maxSize)
    }
}