如何设计一个LRU Cache
如何设计一个LRU Cache?
Google和百度的面试题都出现了设计一个Cache的题目,什么是Cache,如何设计简单的Cache,通过搜集资料,本文给出个总结。
通常的问题描述可以是这样:
Question:
[1] Design a layer in front of a system which cache the last n requests and the responses to them from the system.
在一个系统之上设计一个Cache,缓存最近的n个请求以及系统的响应。
what data structure would you use to implement the cache in the later to support following operations.
用什么样的数据结构设计这个Cache才能满足下面的操作呢?
[a] When a request comes look it up in the cache and if it hits then return the response from here and do not pass the request to the system
[b] If the request is not found in the cache then pass it on to the system
[c] Since cache can only store the last n requests, Insert the n+1th request in the cache and delete one of the older requests from the cache
因为Cache只缓存最新的n个请求,向Cache插入第n+1个请求时,从Cache中删除最旧的请求。
[d]Design one cache such that all operations can be done in O(1) – lookup, delete and insert.
Cache简介:
Cache(高速缓存), 一个在计算机中几乎随时接触的概念。CPU中Cache能极大提高存取数据和指令的时间,让整个存储器(Cache+内存)既有Cache的高速度,又能有内存的大容量;操作系统中的内存page中使用的Cache能使得频繁读取的内存磁盘文件较少的被置换出内存,从而提高访问速度;数据库中数据查询也用到Cache来提高效率;即便是Powerbuilder的DataWindow数据处理也用到了Cache的类似设计。Cache的算法设计常见的有FIFO(first in first out)和LRU(least
recently used)。根据题目的要求,显然是要设计一个LRU的Cache。
解题思路:
Cache中的存储空间往往是有限的,当Cache中的存储块被用完,而需要把新的数据Load进Cache的时候,我们就需要设计一种良好的算法来完成数据块的替换。LRU的思想是基于“最近用到的数据被重用的概率比较早用到的大的多”这个设计规则来实现的。
为了能够快速删除最久没有访问的数据项和插入最新的数据项,我们双向链表连接Cache中的数据项,并且保证链表维持数据项从最近访问到最旧访问的顺序。每次数据项被查询到时,都将此数据项移动到链表头部(O(1)的时间复杂度)。这样,在进行过多次查找操作后,最近被使用过的内容就向链表的头移动,而没有被使用的内容就向链表的后面移动。当需要替换时,链表最后的位置就是最近最少被使用的数据项,我们只需要将最新的数据项放在链表头部,当Cache满时,淘汰链表最后的位置就是了。
注: 对于双向链表的使用,基于两个考虑。首先是Cache中块的命中可能是随机的,和Load进来的顺序无关。其次,双向链表插入、删除很快,可以灵活的调整相互间的次序,时间复杂度为O(1)。
查找一个链表中元素的时间复杂度是O(n),每次命中的时候,我们就需要花费O(n)的时间来进行查找,如果不添加其他的数据结构,这个就是我们能实现的最高效率了。目前看来,整个算法的瓶颈就是在查找这里了,怎么样才能提高查找的效率呢?Hash表,对,就是它,数据结构中之所以有它,就是因为它的查找时间复杂度是O(1)。
梳理一下思路:对于Cache的每个数据块,我们设计一个数据结构来储存Cache块的内容,并实现一个双向链表,其中属性next和prev时双向链表的两个指针,key用于存储对象的键值,value用户存储要cache块对象本身。
Cache的接口:
查询:
- 根据键值查询hashmap,若命中,则返回节点,否则返回null。
- 从双向链表中删除命中的节点,将其重新插入到表头。
- 所有操作的复杂度均为O(1)。
插入:
- 将新的节点关联到Hashmap
- 如果Cache满了,删除双向链表的尾节点,同时删除Hashmap对应的记录
- 将新的节点插入到双向链表中头部
更新:
- 和查询相似
删除:
- 从双向链表和Hashmap中同时删除对应的记录。
LRU Cache的Java 实现:
public interface Cache<K extends Comparable, V> {
V get(K obj); //查询
void put(K key, V obj); //插入和更新
void put(K key, V obj, long validTime);
void remove(K key); //删除
Pair[] getAll();
int size();
}
public class Pair<K extends Comparable, V> implements Comparable<Pair> {
public Pair(K key1, V value1) {
this.key = key1;
this.value = value1;
}
public K key;
public V value;
public boolean equals(Object obj) {
if(obj instanceof Pair) {
Pair p = (Pair)obj;
return key.equals(p.key)&&value.equals(p.value);
}
return false;
}
@SuppressWarnings("unchecked")
public int compareTo(Pair p) {
int v = key.compareTo(p.key);
if(v==0) {
if(p.value instanceof Comparable) {
return ((Comparable)value).compareTo(p.value);
}
}
return v;
}
@Override
public int hashCode() {
return key.hashCode()^value.hashCode();
}
@Override
public String toString() {
return key+": "+value;
}
}
public class LRUCache<K extends Comparable, V> implements Cache<K, V>,
Serializable {
private static final long serialVersionUID = 3674312987828041877L;
Map<K, Item> m_map = Collections.synchronizedMap(new HashMap<K, Item>());
Item m_start = new Item(); //表头
Item m_end = new Item(); //表尾
int m_maxSize;
Object m_listLock = new Object(); //用于并发的锁
static class Item {
public Item(Comparable k, Object v, long e) {
key = k;
value = v;
expires = e;
}
public Item() {}
public Comparable key; //键值
public Object value; //对象
public long expires; //有效期
public Item previous;
public Item next;
}
void removeItem(Item item) {
synchronized(m_listLock) {
item.previous.next = item.next;
item.next.previous = item.previous;
}
}
void insertHead(Item item) {
synchronized(m_listLock) {
item.previous = m_start;
item.next = m_start.next;
m_start.next.previous = item;
m_start.next = item;
}
}
void moveToHead(Item item) {
synchronized(m_listLock) {
item.previous.next = item.next;
item.next.previous = item.previous;
item.previous = m_start;
item.next = m_start.next;
m_start.next.previous = item;
m_start.next = item;
}
}
public LRUCache(int maxObjects) {
m_maxSize = maxObjects;
m_start.next = m_end;
m_end.previous = m_start;
}
@SuppressWarnings("unchecked")
public Pair[] getAll() {
Pair p[] = new Pair[m_maxSize];
int count = 0;
synchronized(m_listLock) {
Item cur = m_start.next;
while(cur!=m_end) {
p[count] = new Pair(cur.key, cur.value);
++count;
cur = cur.next;
}
}
Pair np[] = new Pair[count];
System.arraycopy(p, 0, np, 0, count);
return np;
}
@SuppressWarnings("unchecked")
public V get(K key) {
Item cur = m_map.get(key);
if(cur==null) {
return null;
}
//过期则删除对象
if(System.currentTimeMillis()>cur.expires) {
m_map.remove(cur.key);
removeItem(cur);
return null;
}
if(cur!=m_start.next) {
moveToHead(cur);
}
return (V)cur.value;
}
public void put(K key, V obj) {
put(key, obj, -1);
}
public void put(K key, V value, long validTime) {
Item cur = m_map.get(key);
if(cur!=null) {
cur.value = value;
if(validTime>0) {
cur.expires = System.currentTimeMillis()+validTime;
}
else {
cur.expires = Long.MAX_VALUE;
}
moveToHead(cur); //成为最新的对象,移动到头部
return;
}
if(m_map.size()>=m_maxSize) {
cur = m_end.previous;
m_map.remove(cur.key);
removeItem(cur);
}
long expires=0;
if(validTime>0) {
expires = System.currentTimeMillis()+validTime;
}
else {
expires = Long.MAX_VALUE;
}
Item item = new Item(key, value, expires);
insertHead(item);
m_map.put(key, item);
}
public void remove(K key) {
Item cur = m_map.get(key);
if(cur==null) {
return;
}
m_map.remove(key);
removeItem(cur);
}
public int size() {
return m_map.size();
}
}
如何设计一个LRU Cache的更多相关文章
- 字节面试问我如何高效设计一个LRU,当场懵
首发公众号:bigsai 转载请放置作者和原文(本文)链接 前言 大家好,我是bigsai,好久不见,甚是想念! 最近有个小伙伴跟我诉苦,说他没面到LRU,他说他很久前知道有被问过LRU的但是心想自己 ...
- 设计并实现一个LRU Cache
一.什么是Cache 1 概念 Cache,即高速缓存,是介于CPU和内存之间的高速小容量存储器.在金字塔式存储体系中它位于自顶向下的第二层,仅次于CPU寄存器.其容量远小于内存,但速度却可以接近CP ...
- [转]如何用C++实现一个LRU Cache
[转自http://hawstein.com/posts/lru-cache-impl.html] LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法 ...
- 用 Go 实现一个 LRU cache
前言 早在几年前写过关于 LRU cache 的文章: https://crossoverjie.top/2018/04/07/algorithm/LRU-cache/ 当时是用 Java 实现的,最 ...
- 动手实现一个 LRU cache
前言 LRU 是 Least Recently Used 的简写,字面意思则是最近最少使用. 通常用于缓存的淘汰策略实现,由于缓存的内存非常宝贵,所以需要根据某种规则来剔除数据保证内存不被撑满. 如常 ...
- LRU Cache
LRU Cache 题目链接:https://oj.leetcode.com/problems/lru-cache/ Design and implement a data structure for ...
- 【Leetcode146】LRU Cache
问题描述: 设计一个LRU Cache . LRU cache 有两个操作函数. 1.get(key). 返回cache 中的key对应的 val 值: 2.set(key, value). 用伪代码 ...
- LRU Cache leetcode java
题目: Design and implement a data structure for Least Recently Used (LRU) cache. It should support the ...
- Redis(八) LRU Cache
Redis(八)-- LRU Cache 在计算机中缓存可谓无所不在,无论还是应用还是操作系统中,为了性能都需要做缓存.然缓存必然与缓存算法息息相关,LRU就是其中之一.笔者在最先接触LRU是大学学习 ...
随机推荐
- 05 SpringMVC:02.参数绑定及自定义类型转换&&04.SpringMVC返回值类型及响应数据类型&&05.文件上传&&06.异常处理及拦截器
springMVC共三天 第一天: 01.SpringMVC概述及入门案例 02.参数绑定及自定义类型转换 03.SpringMVC常用注解 第二天: 04.SpringMVC返回值类型及响应数据类型 ...
- Java之解决线程安全问题的方式三:Lock锁
import java.util.concurrent.locks.ReentrantLock; /** * 解决线程安全问题的方式三:Lock锁 --- JDK5.0新增 * * 1. 面试题:sy ...
- Tensorflow学习教程------下载图像识别模型inceptionV3
# coding: utf-8 import tensorflow as tf import os import tarfile import requests #inception模型下载地址 in ...
- Maven--远程仓库的认证
大部分远程仓库无须认证就可以访问,但有时候出于安全方面的考虑,我们需要提供认证信息才能访问一些远程仓库. 配置认证信息和配置仓库信息不同,仓库信息可以直接配置在 POM 文件中,但是认证信息必须配置在 ...
- linux服务器开放防火墙和端口,以及查询状态
自己搞一个自己网站时候,购买的阿里云服务器,发现部署项目访问不到,首先需要确认入站规则是否配置. 一.安全组列表添加 1.打开安全组列表 2.添加入站规则 3.添加安全组规则 二.通过防火墙,开启端口 ...
- ant design for vue 刷新页面,根据当前路由选中相应菜单
<a-menu theme="dark" mode="horizontal" class="menu__a" @select=&quo ...
- ansible puppet saltstack三款自动化运维工具的对比
一.基础介绍 ansible基础介绍可参考:http://www.linuxidc.com/Linux/2017-12/149671.htm puppet基础介绍可参考:http://www.linu ...
- pyinstaller 3.6版本通过pip安装失败的解决办法
本机中原pyinstaller版本为3.5版本,本打算通过 pip install --upgrade pyinstaller进行升级,竟然报错,后面卸载再重新安装也一样报错,没办法看来通过pip是暂 ...
- 扯下Python的super()
注: Python 2.7.x 环境下 今晚搜东西无意中看到这篇Understanding Python super() with __init__() methods. 其实这篇老早就看过了, 不过 ...
- Spring4.3.25版本使用的积累性总结(不定期更新)
Spring4.3.25版本使用的积累性总结 Spring4.x所有Maven依赖 Spring基于XML配置方式注入bean对象和@Resource注解的使用 详解Spring3.x 升级至 Spr ...