LRU缓存设计

193 阅读6分钟

LRU Least Recently Used 缓存淘汰算法:

最近最少使用缓存的设计原则:

在内存不足的情况下,通常的做法是将更早和不经常使用的数据删掉释放内存给其他需要访问的数据。 访问数据的方式可以是set也可以是get。创建实际上属于set、写数据属于set获取数据属于get。

在每一次访问数据的时候 要先进行校验:

数据结构组成如下:

  1. 缓存字典 存放key-value键值对
  2. 优先级链表 链表中的node以key为node的value
  3. key-node字典 为了方便通过key寻找node

写入操作时

判断当前key是否已经存在字典缓存 存在就删除字典缓存中的key-value

容量判断:通过优先级链表中的node个数进行(这里是模拟,真实情况是使用字节大小 个数*步幅)如果超标就通过优先级链表获取结尾处的node的value (这个value实际上就是key)

删除字典缓存的key-value  删除优先级最后一个node 删除记录key-node字典中的key-node键值对*

然后执行添加操作 添加操作就是将key-value,key-node插入到对应的两个字典中 在优先级链表头部插入这个key-node的node节点*

读取操作时 删除链表优先级中的key对应的 然后删除key-node字典中的key-node键值对 然后重新将node添加到优先级链表头部 将key-node键值对重新添加到keytoNode字典

代码:

import Foundation
public final class LinkedList<T> {
   // node
   public class LinkedListNode<T> {

        var value: T

        // 下一个位置node
        var next: LinkedListNode?

        // 上一个位置node
        weak var previous: LinkedListNode?
        
        public init(value: T) {
        self.value = value
        }
    }

  // 类型声明
    public typealias Node = LinkedListNode<T>

    // 链表头
    fileprivate var head: Node?
    
    public init() {}

    // 判断链表是否为空
    public var isEmpty: Bool {

    return head == nil

    }

   // 返回链表头
    public var first: Node? {

        return head
    }

   // 返回链表尾
   public var last: Node? {

        if var node = head {

            while let next = node.next {

                node = next
            }

            return node

        } else {

            return nil
        }
    }

  // 链表中node的个数
    public var count: Int {

        if var node = head {
            var c = 1
            while let next = node.next {
                node = next
                c += 1
            }
            return c
        } else {
            return 0
        }
    }

  // 获取指定位置的node

    public func node(atIndex index: Int) -> Node? {
        if index >= 0 {
            var node = head
            var i = index

            while node != **nil** {

                if i == 0 { **return** node }

                i -= 1

                node = node!.next
            }
        }
        return nil
    }

  // 下标方法 让link可以向数组那样直接通过下标访问到对应的成员或者其他类型

    public subscript(index: Int) -> T {

        let node = self.node(atIndex: index)

        assert(node != **nil**)

        return node!.value

    }
    public func append(_ value: T) {

        let newNode = Node(value: value)

        self.append(newNode)

    }
    public func append(_ node: Node) {

        let newNode = LinkedListNode(value: node.value)

        if let lastNode = last {

            newNode.previous = lastNode

            lastNode.next = newNode

        } else {

            head = newNode

        }

    }
    public func append(_ list: LinkedList) {

        var nodeToCopy = list.head

        while let node = nodeToCopy {

            self.append(node.value)

            nodeToCopy = node.next

        }

    }

// 获取的是index对应的node和这个node的前一个位置

    private func nodesBeforeAndAfter(index: Int) -> (Node?, Node?) {

        assert(index >= 0) // index为0对应的是链表头node

        var i = index

        var next = head
        var prev: Node?

      // 从链表头开始
        while next != nil && i > 0 {

            i -= 1

            prev = next

            next = next!.next
        }
        assert(i == 0// if > 0, then specified index was too large
      return (prev, next)
    }
    public func insert(_ value: T, atIndex index: Int) {

        let newNode = Node(value: value)

        self.insert(newNode, atIndex: index)

    }

// 将node插入到指定位置

    public func insert(_ node: Node, atIndex index: Int) {

        let (prev, next) = nodesBeforeAndAfter(index: index)

        let newNode = LinkedListNode(value: node.value)

        newNode.previous = prev

        newNode.next = next

        prev?.next = newNode

        next?.previous = newNode
        
        if prev == nil {

            head = newNode
        }
    }

   // 将一个链表插入到当前链表的指定位置
    public func insert(_ list: LinkedList, atIndex index: Int) {

        if list.isEmpty { return }

        var (prev, next) = nodesBeforeAndAfter(index: index)

        var nodeToCopy = list.head

        var newNode: Node?

        while let node = nodeToCopy {

            newNode = Node(value: node.value)

            newNode?.previous = prev

            if let previous = prev {

                previous.next = newNode

            } else {

                self.head = newNode

            }
            nodeToCopy = nodeToCopy?.next

            prev = newNode
        }

        prev?.next = next
        next?.previous = prev
    }

   // 移除链表中所有的node 实际上将链表表头设为nil
    public func removeAll() {

        head = nil

        // 如果链表结构中声明了tail作为链表结尾 在这个方法中就还需要tail = nil
    }
    // 删除指定的node
    @discardableResult public func remove(node: Node) -> T {

        let prev = node.previous

        let next = node.next

        if let prev = prev {

            prev.next = next

        } else {

            head = next
        }

        next?.previous = prev
        node.previous = nil

        node.next = nil

        return node.value
    }

@discardableResult public func removeLast() -> T {

        assert(!isEmpty)

        return remove(node: last!)
    }

    @discardableResult public func remove(atIndex index: Int) -> T {

        let node = self.node(atIndex: index)

        assert(node != **nil**)

        return remove(node: node!)
    }
}

extension LinkedList: CustomStringConvertible {

    public var description: String {

        var s = "["
        var node = head

        while node != nil {

            s += "\(node!.value)"

            node = node!.next

            if node != nil { s += ", " }
        }

        return s + "]"

    }

}

extension LinkedList {

    // 链表翻转
public func reverse() {

        var node = head

        while let currentNode = node {

            node = currentNode.next

            swap(&currentNode.next, &currentNode.previous)

            head = currentNode

        }
    }
}

extension LinkedList {

    public func map<U>(transform: (T) -> U) -> LinkedList<U> {

        let result = LinkedList<U>()

        var node = head

        while node != nil {

            result.append(transform(node!.value))

            node = node!.next

        }

        return result
    }
    
public func filter(predicate: (T) -> Bool) -> LinkedList<T> {

        let result = LinkedList<T>()

        var node = head
        while node != nil{

            if predicate(node!.value) {

                result.append(node!.value)

            }

            node = node!.next

        }

        return result
    }
}

extension LinkedList {

    convenience init(array: Array<T>) {

        self.init()
        for element in array {

            self.append(element)

        }
    }
}

extension LinkedList: ExpressibleByArrayLiteral {

  public convenience init(arrayLiteral elements: T...) {

    self.init()
    for element in elements {

      self.append(element)

    }
  }
}
import Foundation

// 复习Hashable协议的实现
public class LRUCache<KeyType: Hashable> {

    // 缓存最大值

    private let maxSize: Int

    // 缓存实体 这里使用字典来实现

    private var cache: [KeyType: Any] = [:]

    // 优先级 链表 每次访问一个元素不论是get还是set 都将这个元素加入到这个链表中
    private var priority: LinkedList<KeyType> = LinkedList<KeyType>()

    // 字典 建立key到链表中的node的键值对应关系

    private var key2node: [KeyType: LinkedList<KeyType>.LinkedListNode<KeyType>] = [:]
    
    public init(_ maxSize: Int) {

        self.maxSize = maxSize

    }

    public func get(_ key: KeyType) -> Any? {

        guard let val = cache[key] else {

            return nil

        }

        remove(key)

        insert(key, val: val)
        
        return val
    }

/**

      set函数:当写入一个key-value时

             1. 先检查当前缓存字典中是否存在这个key 存在就先删除

             2  如果优先级链中以key为值的node个数超过RLU最大容量,就从优先级链表中获取最后一个node

               的值(注意 node的值实际上就是key)

 */

    public func set(_ key: KeyType, val: **Any**) {

        if cache[key] != nil {

            // 当前缓存字典中有这个key 就执行三个删除操作 缓存字典删除 优先级链表删除 key-node字典删除 下同
            remove(key)

        } else if priority.count >= maxSize, **let** keyToRemove = priority.last?.value {

            // 缓存溢出时 找到优先级队列中最后一个key 执行三个删除操作

            remove(keyToRemove)
        }

       // 将参数key键值对存放进缓存字典 键存放进优先级字典
        insert(key, val: val)
    }
    // 从cache key2Node删除键值对 从priority(实际上是链表)中删除值(实际上是node)

    private func remove(_ key: KeyType) {

        // 删除字典中给定的key以及对应的value

        cache.removeValue(forKey: key)

        // key2node中没有key对应的node 直接返回

        guard let node = key2node[key] **else** {

            return
        }

        // 单向链表删除node

        priority.remove(node: node)

        // 删除key2node字典中的key-value键值对

        key2node.removeValue(forKey: key)

    }
    // 对应remove函数 缓存字典设定键值对 将key插入到链表头
    private func insert(_ key: KeyType, val: Any) {

        cache[key] = val

        // 将key作为node的值创建node插入到链表表头
        priority.insert(key, atIndex: 0)

        // 如果表头为空 就直接返回
        guard let first = priority.first **else** {

            return

        }

        // key-node存入另一个字典

        key2node[key] = first
    }
}
func lruDemo() {

        

        let cache = LRUCache<String>(2)

        cache.set("a", val: 1)

        cache.set("b", val: 2)

        cache.get("a") // returns 1

        cache.set("c", val: 3) // evicts key "b"

        cache.get("b") // returns nil (not found)

        cache.set("d", val: 4) // evicts key "a"

        cache.get("a") // returns nil (not found)

        cache.get("c") // returns 3

        cache.get("d") // returns 4

    }