Overivew
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
- 新数据插入到链表头部;
- 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
- 当链表满的时候,将链表尾部的数据丢弃。
其过程如下:
- 最开始时,内存空间是空的,因此依次进入A、B、C是没有问题的
- 当加入D时,就出现了问题,内存空间不够了,因此根据LRU算法,内存空间中A待的时间最为久远,选择A,将其淘汰
- 当再次引用B时,内存空间中的B又处于活跃状态,而C则变成了内存空间中,近段时间最久未使用的
- 当再次向内存空间加入E时,这时内存空间又不足了,选择在内存空间中待的最久的C将其淘汰出内存,这时的内存空间存放的对象就是E->B->D
Swift 实现
可以看看,github.com/nicklockwoo… 的实现
创建一个Container,这是一个双端队列
final class Container {
var value: Value
var cost: Int
let key: Key
unowned(unsafe) var prev: Container?
unowned(unsafe) var next: Container?
init(value: Value, cost: Int, key: Key) {
self.value = value
self.cost = cost
self.key = key
}
}
定义其删除节点和添加节点操作
// Remove container from list
func remove(_ container: Container) {
if head === container { //如果待删除节点等于头节点,则头部节点向下移动
head = container.next
}
if tail === container {//如果待删除节点等于尾节点
tail = container.prev
}
// 清空无效链接
container.next?.prev = container.prev
container.prev?.next = container.next
container.next = nil
}
// Append container to list 将节点加到链表尾部
func append(_ container: Container) {
assert(container.next == nil)
if head == nil {
head = container
}
container.prev = tail
tail?.next = container
tail = container
}
还有清空链表内容, 注意这里的lock
func clean() {
lock.lock()
defer { lock.unlock() }
while totalCost > totalCostLimit || count > countLimit,
let container = head
{
remove(container)
values.removeValue(forKey: container.key)
totalCost -= container.cost
}
}
下面看一下整体的构造,
private var values: [Key: Container] = [:]
private unowned(unsafe) var head: Container?
private unowned(unsafe) var tail: Container?
var count: Int { values.count }
var isEmpty: Bool { values.isEmpty }
其中,values
包括数据的key
和指向对应的队列节点
当有新数据要加入缓存时,调用setValue
,
func setValue(_ value: Value?, forKey key: Key, cost: Int = 0) {
guard let value = value else { // 如果存在
removeValue(forKey: key)
return
}
lock.lock()
if let container = values[key] {
container.value = value
totalCost -= container.cost
container.cost = cost
remove(container)
append(container)
} else {
let container = Container(
value: value,
cost: cost,
key: key
)
values[key] = container
append(container)
}
totalCost += cost
lock.unlock()
clean()
}
当要删除某个数据时,
func removeValue(forKey key: Key) -> Value? {
lock.lock()
defer { lock.unlock() }
guard let container = values.removeValue(forKey: key) else {
return nil
}
remove(container)
totalCost -= container.cost
return container.value
}
里面加入锁机制 是为了保证LRUCache的并发。