http://flychao88.iteye.com/blog/1977653文章中介绍了常见的几种缓存淘汰策略

LRU:least recently used,最近最少使用算法。其实就是按使用时间倒排序,然后从尾部删除元素。它的使用场景是:在有限的空间中存储对象时,当空间满时,会按一定的原则删除原有的对象,常用的原则(算法)有LRU,FIFO,LFU等。在计算机的Cache硬件,以及主存到虚拟内存的页面置换,还有Redis缓存系统中都用到了该算法。我在一次面试和一个笔试时,也遇到过这个问题。

LRU的算法是比较简单的,当对key进行访问时(一般有查询,更新,增加,在get()和set()两个方法中实现即可)时,将该key放到队列的最前端(或最后端)就行了,这样就实现了对key按其最后一次访问的时间降序(或升序)排列,当向空间中增加新对象时,如果空间满了,删除队尾(或队首)的对象。

在Python中,可以使用collections.OrderedDict很方便的实现LRU算法,当然,如果你想不到用OrderedDict,那可以用dict+list来实现。本文主要参考了LRU CACHE IN PYTHON,写的非常好,既实现了功能,又简洁易读。方法一的代码与参考文章基本相同,方法二是我自己想出来的,比较繁琐一些,其实OrderedDict本身也是类似的这种机制来实现的有序。

不过,下面的实现是有问题的,这个cache的key:value键值对中,value只能是不可变类型。因为,如果value是可变类型,那对于同一个key,所有调用get(key)方法返回的value都是指向同一个可变对象的,当修改其中一个value时,那所有的value都会被修改了,即使你没有调用set()方法也会这样。这是我们不希望看到的。解决方法我想到了两种,一是可变对象序列化后再存储,即将可变对象转为不可变对象;二是仍存储可变对象,但get()时,返回一个深拷贝,这样每个get()调用返回的对象就不会相互影响了。推荐第一种方法。另外,对于key,推荐使用str/unicode类型。

当并发时,还会存在一个问题,因为这涉及到对公共资源的写操作,所以必须要对set()加锁。其实,在并发情况下,所有对公共资源的写操作都要加锁。如果不存在并发的情况,只有单线程,那可以不加锁。

方法一:用OrderedDict实现(推荐)

from collections import OrderedDict

class LRUCache(OrderedDict):
'''不能存储可变类型对象,不能并发访问set()''' def __init__(self,capacity):
self.capacity = capacity
self.cache = OrderedDict() def get(self,key):
if self.cache.has_key(key):
value = self.cache.pop(key)
self.cache[key] = value
else:
value = None return value def set(self,key,value):
if self.cache.has_key(key):
value = self.cache.pop(key)
self.cache[key] = value
else:
if len(self.cache) == self.capacity:
self.cache.popitem(last = False) #pop出第一个item
self.cache[key] = value
else:
self.cache[key] = value

测试代码如下

c = LRUCache(5)

for i in range(5,10):
c.set(i,10*i) print c.cache, c.cache.keys() c.get(5)
c.get(7) print c.cache, c.cache.keys() c.set(10,100)
print c.cache, c.cache.keys() c.set(9,44)
print c.cache, c.cache.keys()

输出如下

OrderedDict([(5, 50), (6, 60), (7, 70), (8, 80), (9, 90)])     [5, 6, 7, 8, 9]
OrderedDict([(6, 60), (8, 80), (9, 90), (5, 50), (7, 70)]) [6, 8, 9, 5, 7]
OrderedDict([(8, 80), (9, 90), (5, 50), (7, 70), (10, 100)]) [8, 9, 5, 7, 10]
OrderedDict([(8, 80), (5, 50), (7, 70), (10, 100), (9, 90)]) [8, 5, 7, 10, 9]

方法二:用dict+list实现(不推荐)

class LRUCache(object):
'''不能存储可变类型对象,不能并发访问set()''' def __init__(self,capacity):
self.l = []
self.d = {}
self.capacity = capacity def get(self,key):
if self.d.has_key(key):
value = self.d[key]
self.l.remove(key)
self.l.insert(0,key)
else:
value = None return value def set(self,key,value):
if self.d.has_key(key):
self.l.remove(key)
elif len(self.d) == self.capacity:
oldest_key = self.l.pop()
self.d.pop(oldest_key) self.d[key] = value
self.l.insert(0, key)

测试代码如下

c = LRUCache(5)

for i in range(5,10):
c.set(i,10*i) print c.d,c.l c.get(5)
c.get(7) print c.d,c.l c.set(10,100)
print c.d,c.l c.set(9,44)
print c.d,c.l

输出为

{8: 80, 9: 90, 5: 50, 6: 60, 7: 70}   [9, 8, 7, 6, 5]
{8: 80, 9: 90, 5: 50, 6: 60, 7: 70} [7, 5, 9, 8, 6]
{5: 50, 7: 70, 8: 80, 9: 90, 10: 100} [10, 7, 5, 9, 8]
{5: 50, 7: 70, 8: 80, 9: 44, 10: 100} [9, 10, 7, 5, 8]

参考:

http://www.kunxi.org/blog/2014/05/lru-cache-in-python/

http://blog.sina.com.cn/s/blog_631d3a630101mhup.html

LRU算法的Python实现的更多相关文章

  1. python模拟页面调度LRU算法

    所谓LRU算法,是指在发生缺页并且没有空闲主存块时,把最近最少使用的页面换出主存块,腾出地方来调入新页面. 问题描述:一进程获得n个主存块的使用权,对于给定的进程访问页面次序,问当采用LRU算法时,输 ...

  2. LRU算法原理解析

    LRU是Least Recently Used的缩写,即最近最少使用,常用于页面置换算法,是为虚拟页式存储管理服务的. 现代操作系统提供了一种对主存的抽象概念虚拟内存,来对主存进行更好地管理.他将主存 ...

  3. Android图片缓存之Lru算法

    前言: 上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小.我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发 ...

  4. 缓存淘汰算法--LRU算法

    1. LRU1.1. 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是"如果数据最近被访问过,那么将来被访问的几率也 ...

  5. 借助LinkedHashMap实现基于LRU算法缓存

    一.LRU算法介绍 LRU(Least Recently Used)最近最少使用算法,是用在操作系统中的页面置换算法,因为内存空间是有限的,不可能把所有东西都放进来,所以就必须要有所取舍,我们应该把什 ...

  6. LinkedHashMap实现LRU算法

    LinkedHashMap特别有意思,它不仅仅是在HashMap上增加Entry的双向链接,它更能借助此特性实现保证Iterator迭代按照插入顺序(以insert模式创建LinkedHashMap) ...

  7. LinkedHashMap 和 LRU算法实现

    个人觉得LinkedHashMap 存在的意义就是为了实现 LRU 算法. public class LinkedHashMap<K,V> extends HashMap<K,V&g ...

  8. 简单LRU算法实现缓存

    最简单的LRU算法实现,就是利用jdk的LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可,如下所示: java 代码 import java.ut ...

  9. 八大排序算法的 Python 实现

    转载: 八大排序算法的 Python 实现 本文用Python实现了插入排序.希尔排序.冒泡排序.快速排序.直接选择排序.堆排序.归并排序.基数排序. 1.插入排序 描述 插入排序的基本操作就是将一个 ...

随机推荐

  1. base64变形注入与联合查询注入的爱情故事

    先来写一下GET的知识点: 1.知道了convart函数(CONVERT函数是把日期转换为新数据类型的通用函数) 2.Illegal mix of collations for operation ' ...

  2. EAGAIN、EWOULDBLOCK、EINTR与非阻塞 长连接

    EAGAIN.EWOULDBLOCK.EINTR与非阻塞 长连接 EWOULDBLOCK用于非阻塞模式,不需要重新读或者写 EINTR指操作被中断唤醒,需要重新读/写 在Linux环境下开发经常会碰到 ...

  3. Scala中集合类型与java中集合类型转换

    对于java中的集合元素并不能在scala中拿来就用的,需要进行相应的转换. 1. 转换规则如下 从下面可以看出,有些可以相互转换的,有些只能单向转换: scala.collection.Iterab ...

  4. lscpu和cat /proc/cpuinfo

    lscpu的使用 描述: 此命令用来显示cpu的相关信息 lscpu从sysfs和/proc/cpuinfo收集cpu体系结构信息,命令的输出比较易读 命令输出的信息包含cpu数量,线程,核数,套接字 ...

  5. kettle两表内链接的查询结果与sql语句的查询结果不符合?

    1.教师表输入 2.学生表 查 3.学生表中查出的教师id进行排序 5.教师表中查出的同样也对教师的id进行排序 6.进行左连接 总结: 进行连接的时候的关键是同样对教师的id进行先排序

  6. php -- instanceof、class_exists、insterface_exists、method_exists、get_class、get_parent_class

    class_exists:类是否存在 在创建对象之前判断类是否存在,如果不存在就应该先加载类,再创建对象,容错. interface_exists:接口是否存在 method_exists:方法是否存 ...

  7. exif_imagetype() 函数在linux下的php中不存在

    1.问题,项目中上传文件使用插件时,windows上支持函数exif_imagetype(),而在linux上不支持. 2.PHP exif_imagetype的本质 PHP exif_imagety ...

  8. 如何用MathType编辑出积分符号

    MathType由于能够编辑出众多的数学符号而备受理工科学生与老师的喜爱.利用它,你可以在文档中随意编写出你想要的公式.对于从来没有用过公式编辑器的人来说,在文档中看到那些复杂的数学公式时总是会为之惊 ...

  9. Python+selenium之简单介绍unittest单元测试框架

    Python+selenium之简单介绍unittest单元测试框架 一.unittest简单介绍 unittest支持测试自动化,共享测试用例中的初始化和关闭退出代码,在unittest中最小单元是 ...

  10. leetcode -- Unique Binary Search Trees todo

    Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For examp ...