1. LRU简介
LRU(Least recently used,最近最少使用)是缓存置换策略中的一种常用的算法。其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。当缓存队列已满时,新的元素加入队列时,需要从现有队列中移除一个元素,LRU 策略就是将最近最少被访问的元素移除,从而腾出空间给新的元素。
当对key进行访问时(一般有查询,更新,增加,在get()和set()两个方法中实现即可)时,将该key放到队列的最前端(或最后端)就行了,这样就实现了对key按其最后一次访问的时间降序(或升序)排列,当向空间中增加新对象时,如果空间满了,删除队尾(或队首)的对象。
2. 算法实现
2.1 普通实现
借助于普通dict和list来实现,dict保存键值对,list保证插入的有序(借助列表来记录插入的顺序)。
# 基于普通dict和list实现
class LRUCache(object):
def __init__(self, size=5):
self.size = size
self.cache = dict()
self.key_list = []
def get(self, key):
if key in self.cache:
self.key_list.remove(key)
self.key_list.insert(0, key)
return self.cache[key]
else:
return None
def set(self, key, value):
if key in self.cache: # 更新
self.key_list.remove(key)
elif len(self.cache) == self.size: # 删除插入
old_key = self.key_list.pop()
self.cache.pop(old_key)
self.cache[key] = value # 一般插入
self.key_list.insert(0, key)
使用hash表来定位结点位置,get()和set()的时间复杂度均为O(1),空间复杂度为O(n)。
2.2 借助OrderedDict
python中有一个标准库的类的OrderedDict(有序字典),该类有以下两个方法用来实现LRU算法就十分简单:
1. popitem(last=True):有序字典的 popitem() 方法移除并返回一个 (key, value) 键值对。 如果 last 值为真,则按 LIFO 后进先出的顺序返回键值对,否则就按 FIFO 先进先出的顺序返回键值对。
2. move_to_end(key, last=True):将现有 key 移动到有序字典的任一端。 如果 last 为真值(默认)则将元素移至末尾;如果 last 为假值则将元素移至开头。如果 key 不存在则会触发 KeyError。
from collections import OrderedDict, defaultdict
class LRU:
def __init__(self, capacity=128):
self.capacity = capacity # 缓存容量
self.cache = OrderedDict() # 有序字典缓存
def put(self, key, value):
if key in self.cache:
# 若数据已存在,表示命中一次,需要把数据移到缓存队列末端
self.cache.move_to_end(key)
return
if len(self.cache) >= self.capacity:
# 若缓存已满,则需要淘汰最早没有使用的数据
self.cache.popitem(last=False)
# 录入缓存
self.cache[key]=value
# 遍历key
def travel(self):
for key in self.cache.keys():
print(key)
def get(self, key):
if key in self.cache:
val = self.cache.pop(key)
self.cache[key] = val
else:
val = None
return val
3. 测试
test = LRUCache()
test.set('a', 1)
test.set('b', 2)
test.set('c', 3)
test.set('d', 4)
test.set('e', 5)
# test.set('f',6)
print(test.get('a'))
print(test.set('b1', 55))
print(test.get('e'))
print(test.key_list)
for k, v in test.cache.items():
print(k, "\t", v)