LRU缓存科普与实现(Kotlin 与 Swift) - 实践

news/2025/11/7 12:57:03/文章来源:https://www.cnblogs.com/yxysuanfa/p/19199342

LRU缓存科普与实现(Kotlin 与 Swift)

什么是 LRU?

为什么需要 LRU?

  • 内存受限场景:移动端、嵌入式设备、服务端实例都有严格的内存预算。
  • 性能优化:减少重复的磁盘/网络请求,提高响应速度与吞吐。
  • 常见应用:
    • 图片/资源缓存(如列表滚动时的图片)
    • 数据库查询结果缓存、HTTP 请求结果缓存
    • 浏览器缓存、编译器/解释器内部缓存等

原理与数据结构

  • 经典实现:哈希表(HashMap)+双向链表(Doubly Linked List)。
    • HashMap 提供 O(1) 的查找,定位链表中的节点。
    • 双向链表维护使用顺序:
      • 链表头(Head):最近使用(MRU)
      • 链表尾(Tail):最久未使用(LRU)
  • 操作语义:
    • get(key): 如果命中,将该节点移动到链表头;未命中返回 null
    • put(key, value):
      • 若键已存在,更新值并把节点移动到链表头;
      • 若键不存在,创建新节点加到链表头;
      • 如容量超限,淘汰尾节点(LRU)。
  • 复杂度:get/put 均为 O(1)。

两种常见实现方式

  • 自行实现(哈希表 + 双向链表):可控性高,适合教学与扩展。
  • 依赖 LinkedHashMap:设置访问顺序 accessOrder=true 并重写 removeEldestEntry 即可快速实现,适合简单场景。

API 设计与可见性(Kotlin)

以上可见性在 LruCache.kt 中已明确设置,满足“对外 API 简洁、内部细节封装”的工程化要求。

Kotlin 代码实现(哈希表 + 双向链表)

package algorithms
class LruCache<K, V>(private val capacity: Int) {init { require(capacity > 0) { "capacity must be > 0" } }private data class Node<K, V>(var key: K,var value: V,var prev: Node<K, V>? = null,var next: Node<K, V>? = null)private val map = HashMap<K, Node<K, V>>()private var head: Node<K, V>? = null // MRUprivate var tail: Node<K, V>? = null // LRUfun get(key: K): V? {val node = map[key] ?: return nullmoveToHead(node)return node.value}fun put(key: K, value: V) {val node = map[key]if (node != null) {node.value = valuemoveToHead(node)} else {val newNode = Node(key, value)map[key] = newNodeaddToHead(newNode)if (map.size > capacity) {tail?.let { toRemove ->removeNode(toRemove)map.remove(toRemove.key)}}}}fun remove(key: K): V? {val node = map.remove(key) ?: return nullremoveNode(node)return node.value}fun size(): Int = map.sizefun keys(): List<K> {val list = mutableListOf<K>()var cur = headwhile (cur != null) {list.add(cur.key)cur = cur.next}return list}fun clear() {map.clear()head = nulltail = null}private fun addToHead(node: Node<K, V>) {node.prev = nullnode.next = headhead?.prev = nodehead = nodeif (tail == null) tail = node}private fun removeNode(node: Node<K, V>) {val p = node.prevval n = node.nextif (p != null) p.next = n else head = nif (n != null) n.prev = p else tail = pnode.prev = nullnode.next = null}private fun moveToHead(node: Node<K, V>) {removeNode(node)addToHead(node)}}

使用示例(命令行环境)

  • 假设你的源码在 learning-materials/Algorithms/kotlin/src/main/kotlin/algorithms
package algorithms
fun demoLru() {
val cache = LruCache<Int, String>(capacity = 3)cache.put(1, "A")cache.put(2, "B")cache.put(3, "C")println("初始: ${cache.keys()}") // 3,2,1 (头到尾)cache.get(2) // 访问2,提升优先级println("访问2后: ${cache.keys()}") // 2,3,1cache.put(4, "D") // 容量满,淘汰最久未使用:1println("插入4后: ${cache.keys()}") // 4,2,3cache.put(2, "B2") // 更新2并提升println("更新2后: ${cache.keys()}") // 2,4,3}
  • 你可以在 Main.kt 调用 demoLru(),或单独新建入口。

Kotlin(不使用 Gradle)运行命令

  • 进入目录:cd learning-materials/Algorithms/kotlin
  • 打包运行:
    • kotlinc src/main/kotlin -include-runtime -d app.jar
    • java -jar app.jar
  • 或类路径运行:
    • kotlinc src/main/kotlin -d out
    • kotlin -cp out algorithms.MainKt

复杂度与性能

  • 时间复杂度:get/put 均为 O(1)。
  • 空间复杂度:O(N),N 为缓存容量。
  • 性能建议:
    • 依据实际数据分布与命中率评估容量大小。
    • 热点数据访问频繁时,LRU 能显著提升性能;如存在“扫描型访问”(一次性访问大量不重复数据),可考虑 LFU/ARC 等策略。

边界与常见坑

  • 容量必须 > 0(已在实现中校验)。
  • 更新已存在键时要提升优先级,否则顺序不正确。
  • 注意线程安全:当前实现非线程安全,必要时用外部锁或使用并发容器封装。
  • 对象释放:清理时确保不再被链表引用,避免内存泄漏(实现中已断开指针)。

与其他策略对比

Swift 语法要点(与示例相关)

  • 泛型约束与字典键:LruCache<Key: Hashable, Value>Key: Hashable 要求键可哈希,才能作为 Dictionary 的键([Key: Node])。
  • 参数标签与 _get(_ key: Key) 通过 _ 省略外部标签,调用更自然;put(key:value:) 保留标签以提升可读性。
  • 实例引用:Swift 使用 self 指代当前实例(类似 Kotlin/Java 的 this)。在初始化器与闭包中常显式写 self 以消除歧义。
  • 访问控制与 finalpublic/internal/private 控制可见性;final class 禁止继承,利于内联优化与语义稳定。
  • 可选类型与早退:返回 Value? 表示可能为 nilguard let/if let 常用于安全解包并早退。
  • 结果可丢弃:@discardableResult 允许忽略返回值(本文用于 remove)。
  • 链表维护私有化:addToHeadremoveNodemoveToHead 作为私有方法,确保双向链表在任何更新后保持一致性。

Swift 代码实现(HashMap + 双向链表)

import Foundation
public final class LruCache<Key: Hashable, Value> {private let capacity: Intprivate final class Node {var key: Keyvar value: Valuevar prev: Node?var next: Node?init(key: Key, value: Value) { self.key = key; self.value = value }}private var map: [Key: Node] = [:]private var head: Node? // MRUprivate var tail: Node? // LRUpublic init(capacity: Int) {precondition(capacity > 0, "capacity must be > 0")self.capacity = capacity}public func get(_ key: Key) -> Value? {guard let node = map[key] else { return nil }moveToHead(node)return node.value}public func put(key: Key, value: Value) {if let node = map[key] {node.value = valuemoveToHead(node)} else {let newNode = Node(key: key, value: value)map[key] = newNodeaddToHead(newNode)if map.count > capacity, let toRemove = tail {removeNode(toRemove)map.removeValue(forKey: toRemove.key)}}}@discardableResultpublic func remove(_ key: Key) -> Value? {guard let node = map.removeValue(forKey: key) else { return nil }removeNode(node); return node.value}public var size: Int { map.count }internal func keys() -> [Key] {var result: [Key] = []; var cur = headwhile let n = cur { result.append(n.key); cur = n.next }return result}public func clear() { map.removeAll(); head = nil; tail = nil }private func addToHead(_ node: Node) {node.prev = nil; node.next = headhead?.prev = node; head = nodeif tail == nil { tail = node }}private func removeNode(_ node: Node) {let p = node.prev, n = node.nextif let p = p { p.next = n } else { head = n }if let n = n { n.prev = p } else { tail = p }node.prev = nil; node.next = nil}private func moveToHead(_ node: Node) { removeNode(node); addToHead(node) }}

使用示例(SwiftPM)

func demoLruSwift() {
let cache = LruCache<Int, String>(capacity: 3)cache.put(key: 1, value: "A")cache.put(key: 2, value: "B")cache.put(key: 3, value: "C")print("LRU 初始: \(cache.keys())") // 3,2,1_ = cache.get(2)print("LRU 访问2: \(cache.keys())") // 2,3,1cache.put(key: 4, value: "D")print("LRU 插入4: \(cache.keys())") // 4,2,3cache.put(key: 2, value: "B2")print("LRU 更新2: \(cache.keys())") // 2,4,3}
  • 运行:cd learning-materials/Algorithms/swift && swift run
  • 或在 Xcode 打开 learning-materials/Algorithms/swift(Swift Package)后直接运行。

结语

本文科普了 LRU 的原理与典型实现,给出 Kotlin 与 Swift 代码,并补充了与示例相关的 Swift 语法要点。对于命令行轻量开发,这种实现方式易读、易扩展、易运行。你可以在此基础上继续增加统计、TTL、权重、字节级容量等特性,服务更复杂的业务需求。

科普扩展:工程实践与常见变体

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/958735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十六、Linux网络基础理论 - OSI模型、TCP/IP协议与IP地址详解 - 教程

十六、Linux网络基础理论 - OSI模型、TCP/IP协议与IP地址详解 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: &qu…

Physicians High School Chemistry

Its not physical chemistry. First

深入解析:【Unity】uNet游戏服务端框架(一)服务端架构设计

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Python哈希机制深度解析:从底层原理到工程实践

Python哈希机制深度解析:从底层原理到工程实践 摘要:本文系统剖析Python哈希机制的实现原理、冲突解决策略及数据类型适配规则,通过理论溯源、源码分析与多维度实验验证,揭示其在字典、集合等核心数据结构中的底层…

LaTeX学习笔记:数学公式编辑

LaTeX学习笔记:数学公式编辑LaTeX学习笔记:数学公式编辑 数学公式的编辑一直以来都是我们在撰写科学类文章过程中所面临的最大麻烦之一,相信大家之前应该都尝试过各种在电子文档中输入数学公式的方法,但这些方法多…

容器未正确挂载指定目录 (通常与 目录不存在、权限不足、路径拼写错误 或 系统安全策)

容器未正确挂载指定目录 (通常与 目录不存在、权限不足、路径拼写错误 或 系统安全策) 第一个参数: -v 特别注意⚠️: 以上 -v ~/zapas/dbdata/mssql:/var/opt/mssql/data ~ 等于 /home/当前用户目录 即等于…

飞牛OS Root用户SSH公钥登录完整教程

飞牛OS Root用户SSH公钥登录完整教程 前言 飞牛OS(fnOS)是一款基于Debian的NAS操作系统,提供了强大的数据存储和管理功能。在日常使用和管理过程中,SSH远程登录是必不可少的重要工具。然而,出于安全考 虑,飞牛OS…

红黑树简

package J_TreeSet;import java.util.Objects;public class B_Student implements Comparable<B_Student>{private String name;private int age;public B_Student() {}public B_Student(String name, int age) …

⸢ 柒-Ⅲ⸥⤳ 可信纵深防御建设方案:数据使用可信端安全可信 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【日记】我从来没见过有酒店这么设计的(533 字)

正文出差结束,终于回家了。回到自己的小房间之后,仔仔细细想了一下,发现自己有好多事情都没做。屯了好多经济学的文章没有看、内科学的笔记、买菜、写文章(日记、学海计划、实用类随记、计算机指南,今年的年度总结…

第181-182天:横向移动篇PTH哈希PTT票据PTK密匙Kerberoast攻击点TGTNTLM爆破

首要知识点: pass the hash(哈希传递攻击,简称pth) pass the ticket(票据传递攻击,简称ptt) pass the key(密钥传递攻击,简称ptk) PTH(pass the hash) #利用的lm或ntlm的值进行的渗透测试(NTLM认证攻击)…

2025年上海装修设计标杆公司最新推荐:中古风装修/轻法式装修/现代简约装修/极简风装修、上海千祥建筑定义品质居住新标准

随着城市化进程深化及居民对居住品质要求的提升,家装行业正经历从基础施工向设计美学、环保健康与全程服务一体化的转型。2025年,装修需求预计进一步释放,但市场上装修公司设计水平、施工质量、材料环保标准及售后服…

AI在开源情报搜集系统中的应用汇总

大模型在开源情报系统中的应用已贯穿采集、处理、分析、决策全链条,形成了一个从“数据获取”到“认知生成”的智能闭环。上述功能体系不仅提升了情报工作的效率与深度,更推动了情报模式从“被动检索”向“主动洞察”…

清理docker的overlay2目录

使用Docker时,镜像和容器数据都存储在Docker的存储目录中,默认是/var/lib/docker。在Docker使用overlay2存储驱动时,/var/lib/docker/overlay2目录包含了overlay2存储驱动所使用的文件和目录。 overlay2是Docker的一…

升鲜宝生鲜配送供应链管理系统---PMS--商品品牌多语言存储与 Redis 缓存同步实现

升鲜宝生鲜配送供应链管理系统---PMS--商品品牌多语言存储与 Redis 缓存同步实现 商品品牌多语言存储与 Redis 缓存同步实现文档 本设计文档说明商品品牌(pms_brand)在支持多语言环境下的数据存储、翻译同步及 Redis…

网站在苹果 Safari 进行适配遇到的问题

在网站进行移动端 Web 适配开发中,弹窗和导航栏弹出等常常会出现一些问题,如果是奇奇怪怪的客户严格要求的话,那么就会有下面这些情况:打开弹窗后页面自动放大,视图区被放大到看不全 打开对话框打开后背景仍然能滚…

Python对象模型的认知陷阱:类的`__name__`属性与名字绑定的本质辨析

Python对象模型的认知陷阱:类的__name__属性与名字绑定的本质辨析 摘要:本文通过一个典型的元类使用错误,深入剖析Python对象模型中“类的名称属性”与“名字绑定”这两个常被混淆的核心概念。许多开发者在动态创建…

Python环境教程(三)-环境高级之uv pixi

UV 官网:uv 中文文档 Github地址:astral-sh/uv: An extremely fast Python package and project manager, written in Rust. 什么是uv? uv 是由 Astral 公司开发的一款 Rust 编写的 Python 包管理器和环境管理器,它…

升鲜宝生鲜配送供应链管理系统---PMS 商品模块 + 动态翻译设计说明

PMS 商品模块 + 动态翻译设计说明书 一、模块总体定位 PMS(Product Master System)是供应链体系的商品主数据中心,负责统一维护商品的品牌、分类、标签、单位、材质、存储方式、分拣区域、SPU、SKU 及多语言翻译。 …

深入浅出 SPA/MPA

概述 在 Web 应用架构设计中,单页应用(SPA)与多页应用(MPA)是目前两种主流的前端架构,他们各自适用于不同的业务场景。 作为一个前端开发,理解这两种模式的核心原理、技术实现及优劣势,对于我们未来选择合适的…