zoukankan      html  css  js  c++  java
  • LRU 缓存机制及 3 种简单实现

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

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

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

     

      其中 put 操作用于将最近使用的元素放置在缓存中。必须先判断 key 是否存在,如果存在,则删除,再添加;若不存在,则直接添加,然后判断添加后的缓存是否超过了容量;若超出,则删除最远元素;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         return val;
    24     }
    25 
    26     public void put(int key, int value) {
    27         if(map.containsKey(key)){
    28             // 这个put不能省略,即时key存在,若新添加的value更新了,那刚好就将value更新,如果省略,则value更新不了
    29             map.put(key, value);
    30             Object o = key;
    31             linkedList.remove(o);
    32             linkedList.addLast(key);
    33         }else{
    34             map.put(key, value);
    35             cur_size++;
    36             linkedList.addLast(key);
    37             if(cur_size>max_size){
    38                 int tmp = linkedList.removeFirst();
    39                 map.remove(tmp);
    40                 cur_size--;
    41             }
    42         }
    43     }
    44 }

      其中在进行元素删除的时候,链表的时间复杂度是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
  • 相关阅读:
    前端布局定位
    CSS优化
    CSS工程化
    CSS过渡,动画,2D,3D转换
    CSS,盒子和美化技巧
    HTMl
    定位和布局
    CSS选择器
    八. 实时更新插件 livereload
    七. 浏览器插件 View in Browser
  • 原文地址:https://www.cnblogs.com/dogeLife/p/11370811.html
Copyright © 2011-2022 走看看