明白和实现 LRU 缓存置换算法

鼠扑  金牌会员 | 2024-6-27 07:31:03 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 694|帖子 694|积分 2082

引言

在盘算机科学中,缓存是一种用于提高数据访问速度的技术。然而,缓存空间是有限的,当缓存被填满时,就需要一种策略来决定哪些数据应该保留,哪些应该被淘汰。LRU(迩来最少使用)算法是一种广泛使用的缓存淘汰策略,它基于一个简单的假设:如果数据迩来被访问过,那么它在将来也更有可能被访问。
LRU算法简介

LRU算法的核心思想是:在缓存空间不足时,淘汰最长时间未被访问的数据项。这种策略适用于多种场景,包括Web缓存、数据库查询缓存、操纵体系的页面置换等。
LRU算法的工作原理

LRU算法通常需要两种数据结构来实现:
哈希表:提供O(1)时间复杂度的数据访问和插入。
双向链表:维护数据项的使用序次,迩来使用的在头部,最久未使用的在尾部。
数据访问和插入的流程如下:

获取数据(Get):从缓存中获取数据,如果数据存在(缓存命中),则将该数据移到链表头部;如果数据不存在(缓存未命中),返回 -1。
放入数据(Put):将数据放入缓存,如果数据已经存在,则更新数据值并将该节点移到链表头部;如果数据不存在,则在链表头部插入新的节点,如果缓存已满,还需要移除链表尾部的节点。
LRU算法的实现

  1. class LRUCache {
  2.     /**
  3.         整体思路:定义双向循环链表和Map,其中Map的key存储key,value存储链表节点.
  4.         为什么定义双向循环链表,因为这样定义就不需要定义额外的头尾节点
  5.     */
  6.     static class Node{
  7.         Node prev, next;
  8.         int key, val;
  9.         public Node(int key, int val){
  10.             this.key = key;
  11.             this.val = val;
  12.         }
  13.     }
  14.     private Node dummy = new Node(-1,-1);
  15.    
  16.     Map<Integer, Node> mp = new HashMap<>();
  17.     private int capacity;
  18.     public LRUCache(int capacity) {
  19.         this.capacity = capacity;
  20.         dummy.prev = dummy;
  21.         dummy.next = dummy;
  22.     }
  23.    
  24.     public int get(int key) {
  25.             // 判断是否在缓存
  26.         Node node = mp.get(key);
  27.         // 不在,直接返回-1
  28.         if(node == null) return -1;
  29.         // 在,就把当前节点移动到前面
  30.         moveToHead(node);
  31.         // 返回节点值
  32.         return node.val;
  33.     }
  34.    
  35.     public void put(int key, int value) {
  36.             // 判断是否在缓存
  37.         Node node = mp.get(key);
  38.         // 在,就把当前节点移动到前面
  39.         if(node != null){
  40.                 // 更新node.val
  41.             node.val = value;
  42.             moveToHead(node);
  43.         }else{
  44.                 // 不在,就加入
  45.             node = new Node(key, value);
  46.             mp.put(key, node);
  47.             // 将新节点加入到头部
  48.             insert(node);
  49.             // 如果当前容量大于LRU容量,就移出
  50.             if(mp.size() > capacity){
  51.                     // 找到尾节点
  52.                 node = dummy.prev;
  53.                 // 删除尾节点
  54.                 del(node);
  55.                 // 从缓存移出对应的key
  56.                 mp.remove(node.key);
  57.             }
  58.         }
  59.     }
  60.        
  61.         // 移动当前节点到头节点
  62.     public void moveToHead(Node node){
  63.         del(node);
  64.         insert(node);
  65.     }
  66.         // 插入头部
  67.     public void insert(Node node){
  68.         node.prev = dummy;
  69.         node.next = dummy.next;
  70.         dummy.next.prev = node;
  71.         dummy.next = node;
  72.     }
  73.         // 删除节点
  74.     public void del(Node node){
  75.         node.prev.next = node.next;
  76.         node.next.prev = node.prev;
  77.     }
  78. }
复制代码
LRU算法的上风与范围

LRU算法的上风在于其简单性和效率。它能够快速地识别并淘汰最久未使用的数据项。然而,LRU也有范围性,它可能不适用于所有类型的访问模式,特别是那些具有周期性或随机性访问模式的场景。另外,在某些环境下,LRU 缓存可能会频仍地移除和加载数据,导致缓存抖动。这种现象在缓存大小接近于工作集大小时尤为显着。
结论

LRU算法是一种高效且广泛使用的缓存淘汰策略,适用于多种需要缓存的场景。通过明白其工作原理和实现方式,我们可以更好地使用缓存来提高体系性能。随着技术的发展,对LRU算法的优化和变种也在不停涌现,为不同的应用场景提供了更多的选择。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

鼠扑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表