LRU 缓存机制及 3 种简单实现

  之前好几次接触到 LRU(Least Recently Used)算法,今天来总结下,并用 Java 和 Python 给出相应的实现。

  LRU是一种缓存替换算法,根据字面意思,就是将最近最少使用的页面或者元素进行替换,将最近最多使用的页面或者元素保持在缓存里。有关缓存的知识后面再仔细研究下。由于缓存的容量大小有限,这才有了LRU之类的缓存算法。还有一些其他的缓存算法,可以参考这个页面

  根据下面的图示进行LRU算法的理解。

  其中 put 操作用于将最近使用的元素放置在缓存中,get 操作用于获取缓存中元素的值,在 leetcode146 题中规定,如果缓存中没有该元素,则返回 -1。

  一般我们在实现的时候会考虑存储 key-value 的键值对形式,可以用双链表存储 key,HashMap 存储真正需要的值 value,所以真正意义上的缓存应该是指这个HashMap。链表的作用是用来顺序存储 key,当缓存满了,需要删除最远的那个 key 及其 value,此时就需要根据链表找到最远的 value 的 key,从而删除缓存 HashMap中的最远的键值对。

  这里我们用 双链表 + hashMap 以及 LinkedHashMap 、Python 中 OrderedDict 三种方式来实现一个简单的 LRU 机制。

双链表 + hashMap

 1 class Solution {
 2     private LinkedList<Integer> linkedList;
 3     private Map<Integer, Integer> map;
 4
 5     private int max_size;
 6     private int cur_size = 0;
 7
 8     public Solution(int capacity) {
 9         linkedList = new LinkedList<>();
10         map = new HashMap<>();
11         this.max_size = capacity;
12     }
13
14     public int get(int key) {
15         if(!map.containsKey(key)){
16             return -1;
17         }
18
19         int val = map.get(key);
20         Object o = key;
21         linkedList.remove(o);
22         linkedList.addLast(key);
23
24         return val;
25     }
26
27     public void put(int key, int value) {
28         /**
29          * 必须先判断是否存在,如果存在,则删除,添加新的;
30          * 不存在,则添加,然后判断添加后的缓存是否超过了容量;若超出,则删除最远元素
31          */
32         if(map.containsKey(key)){
33             // 这个put不能省略,即时key存在,若新添加的value更新了,那刚好就将value更新,如果省略,则value更新不了
34             map.put(key, value);
35             Object o = key;
36             linkedList.remove(o);
37             linkedList.addLast(key);
38         }else{
39             map.put(key, value);
40             cur_size++;
41             linkedList.addLast(key);
42
43             // listSet.add(key);
44             if(cur_size>max_size){
45                 // 在满了的时候,必须删除最远的那个,对于 HashMap来讲,他不知道哪个最远,所以需要一个有序的链表来记录
46                 int tmp = linkedList.removeFirst();
47                 map.remove(tmp);
48                 cur_size--;
49             }
50         }
51     }
52 }

  其中在进行元素删除的时候,链表的时间复杂度是O(n),用 HashMap 进行 key 的查找的时候是O(1)的复杂度。

LinkedHashMap

 1 class Solution {
 2     private LinkedHashMap<Integer, Integer> map;
 3     private int max_size;
 4     private int cur_size;
 5
 6     public Solution(int capacity) {
 7         map = new LinkedHashMap<>();
 8         this.max_size = capacity;
 9         this.cur_size = 0;
10     }
11
12     public int get(int key) {
13         // 若没有,则返回 -1
14         if(!map.containsKey(key)){
15             return -1;
16         }
17
18         int val = map.get(key);
19         map.remove(key);
20         map.put(key, val);
21         return val;
22     }
23
24     public void put(int key, int value) {
25         if(map.containsKey(key)){
26             map.remove(key);
27             map.put(key, value);
28         }else{
29             cur_size++;
30             map.put(key, value);
31             if(cur_size > max_size){
32                 int oldestKey = map.keySet().iterator().next(); // 获取最远的key。
33                 map.remove(oldestKey);
34                 cur_size--;
35             }
36         }
37     }
38 }

  LinkedHashMap 本身也是由 双链表 + hashMap 组成的。在缓存满了需要删除最远的元素的时候,是用的 HashMap 里的迭代器来获取最开始进来的key并删除其键值对。

Python OrderedDict

  Python 可以使用这篇文章介绍的 OrderedDict 这一字典子类很轻松的实现 LRU 机制。

 1 class LRUCache:
 2
 3     def __init__(self, capacity: int):
 4         self.dic = OrderedDict()
 5         self.remain = capacity
 6
 7
 8     def get(self, key: int) -> int:
 9         if key not in self.dic:
10             return -1
11         # v = self.dic.pop(key)
12         # self.dic[key] = v
13         self.dic.move_to_end(key, last = True)
14         return self.dic[key]
15
16     def put(self, key: int, value: int) -> None:
17         if key in self.dic:
18             self.dic.pop(key)
19         else:
20             if self.remain > 0:
21                 self.remain -= 1
22             else:
23                self.dic.popitem(last = False)
24         self.dic[key] = value

原文地址:https://www.cnblogs.com/dogeLife/p/11370811.html

时间: 2024-11-09 00:42:04

LRU 缓存机制及 3 种简单实现的相关文章

全面剖析Smarty缓存机制一[三种缓存方式]

今天主要全面总结下Smarty模板引擎中强大的缓存机制,缓存机制有效减少了系统对服务器的压力,而这也是很多开发者喜欢Smarty的原因之一,由于篇幅较大,便于博友阅读,这篇文章将剖析Smarty缓存的几种方式,下篇文章着重讲解下设置缓存及清除缓存的技巧方法(其中包含缓存集合方法). 一.Smarty缓存的几种方式缓存机制中,分为全局缓存.部分缓存.局部缓存三种方式,后面会一一讲述,下面是缓存设置前,Smarty类方法基本目录设置如下:$smarty->Smarty();$smarty->tem

Varnish缓存机制详细介绍及简单配置

Varnish是一款高性能的开源HTTP加速器,其主要用来做为反向代理中的缓存服务器使用,但其实Varnish本身也是具有反向代理功能的,但在创建连接和维持连接上,与Nginx相比差距很大,现在有一个很流行的架构就是前端用Nginx作为反向代理,后面加Varnish缓存服务器为Web服务加速 在将Varnish前先谈谈我们的浏览器缓存机制,现在的浏览器基本都具有缓存功能,它能将我们以前访问过的静态内容和可进行缓存的动态内容缓存再本地,而后在下次访问相同资源时,如果可以确认Server端的资源未发

[LeetCode]146.LRU缓存机制

设计和实现一个 LRU(最近最少使用)缓存 数据结构,使它应该支持以下操作: get 和 put . get(key) - 如果密钥存在于缓存中,则获取密钥的值(总是正数),否则返回 -1.put(key, value) - 如果密钥不存在,请设置或插入值.当缓存达到其容量时,它应该在插入新项目之前使最近最少使用的项目作废. 后续: 你是否可以在 O(1) 时间复杂度中进行两种操作?注:这道题也是2018今日头条春招面试题. 案例: LRUCache cache = new LRUCache(

Hibernate缓存机制和四种状态

临时状态(transient):刚刚用new语句创建,还没有被持久化,不处于Session的缓存中.处于临时状态的Java对象被称为临时对象. 持久化状态(persistent):已经被持久化,加入到Session的缓存中.处于持久化状态的Java对象被称为持久化对象. 游离状态(detached):已经被持久化,但不再处于Session的缓存中.处于游离状态的Java对象被称为游离对象. 删除状态(removed):OID 不为null.从一个Session实例的缓存中删除.被删除对象和数据库

leecode第一百四十六题(LRU缓存机制)

class LRUCache { private: unordered_map<int, list<pair<int,int>>::iterator> _m; // 新节点或刚访问的节点插入表头,因为表头指针可以通过 begin 很方便的获取到. list<pair<int,int>> _list; int _cap; public: LRUCache(int capacity) : _cap(capacity) {} // O(1) // ha

LRU缓存实现(Java)

LRU Cache的LinkedHashMap实现 LRU Cache的链表+HashMap实现 LinkedHashMap的FIFO实现 调用示例 LRU是Least Recently Used 的缩写,翻译过来就是"最近最少使用",LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓

手把手教你构建 Android WebView 的缓存机制 &amp; 资源预加载方案

前言 由于H5具备 开发周期短.灵活性好 的特点,所以现在 Android App大多嵌入了 Android Webview 组件进行 Hybrid 开发 但我知道你一定在烦恼 Android Webview 的性能问题,特别突出的是:加载速度慢 & 消耗流量 今天,我将针对 Android Webview 的性能问题,提出一些有效解决方案. 目录 1. Android WebView 存在什么性能问题? Android WebView 里 H5 页面加载速度慢 耗费流量 下面会详细介绍. 1.

JavaScript算法题实现-146-LRU缓存机制——腾讯面试题库

出题指数(最大5):???? 题目 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1. 写入数据 put(key, value) - 如果密钥已经存在,则变更其数据值:如果密钥不存在,则插入该组「密钥/数据值」.当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值

全面剖析Smarty缓存机制二[清除缓存方法]

前段时间,写了一篇 Smaryt缓存机制的几种缓存方式 ,详细介绍了三种缓存方式:全局缓存.部分缓存.局部缓存,以及通过is_cache()判断是否存在缓存来进行缓存生成.本来这篇早该完成,由于时间关系推到今天,还好思绪没有忘掉,闲话不多说,今天主要讲解Smarty缓存机制中如何清除缓存以及缓存集合的使用技巧,下面步入正题. 一.普通清除缓存方法总所周知,当你看了上一篇文章,会知道通过如下方法,对Smarty的缓存进行清除:代码示例:$smarty->clear_cache("index.