LRU简介
LRU(Least Recently Used),即最近最少使用,是一种常用的页面置换算法
,当需要存入的内容大于临界值的时候,选择最近最少使用的进行淘汰。
如果使用简单的数据结构描述的话,当前有一个栈,但是与栈不同的是,可以随时访问栈中的元素,并且访问过后将当前访问元素置于栈顶。
使用Kotlin写一个LruCache
实现原理
如果写一个缓存的话,一般都是key
,value
类型的,那么自然就会想到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
包含具体使用方法。
创作不易,如有帮助一键三连咯🙆♀️。欢迎技术探讨噢!