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) } }