LRU简介

LRU是“Least Recently Used”的简写,意思是最近最少使用,是一种缓存淘汰策略,在有限的缓存资源中,淘汰掉最近最久未使用的。例如:缓存最大容纳10000条数据,在添加时,只要数据总数小于等于10000可以随意添加,但是当数据量大于1万时,将旧的数据删除,再添加新的数据;添加时,要将新来的数据添加到最前面。说完来添加,再来说查询,从缓存中获取的数据返回之前,需要将数据移动到最前面,因为获取改数据就代表最近使用了,对于缓存来说,最近这个数据很有可能会被再次使用,所以要移动到最前面。


LRU算法分析

因为缓存具有查询快(时间复杂度必须是O(1)),增加快,删除快的特点。同时,LRU策略分析的数据有新旧之分,新的在最前面,旧的往后放。因此选取数据结构时,要遵循有序的特点。因此可以将HashMap与双向链表结合起来实现LRUCache。Hash表查询效率是O(1),但是不是有序的,双向链表(此处不是循环链表)有序并且增加与删除都是O(1),但查询是O(n),所以两者互相取长补短。

实现代码

节点类

public class Node {
public int key,value;
public Node prev;
public Node next; public Node(int key, int value) {
this.key = key;
this.value = value;
} @Override
public String toString() {
return "Node{" +
"key=" + key +
", value=" + value +
'}';
}
}

双向链表

public class DoubleList {
private LinkedList<Node> linkedList = new LinkedList(); /**添加第一个节点
* @param node
*/
public void addFirst(Node node){
linkedList.addFirst(node);
} /**
*移除最后一个节点
*/
public Node removeLast(){
return linkedList.removeLast();
} /**
*移除节点
*/
public void remove(Node node){
linkedList.remove(node);
} /**
* 链表长度
* @return
*/
public int size(){
return linkedList.size();
} /**
* 输出双向链表
*/
public void printAll(){
Iterator<Node> iterator = linkedList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}

LRUCache类

public class LRUCache {
private HashMap<Integer, Node> map;
private DoubleList cache;
private int cap; public LRUCache( int cap) {
this.map = new HashMap<Integer, Node>();
this.cache = new DoubleList();
this.cap = cap;
} /**
* 查询value
**/
public int get(int key){
// 首先判断key是否在map中
if (map.containsKey(key)){
int x = map.get(key).value;
// 在返回之前,将最近访问的键值对添加到LRU中
put(key,x);
return x;
}else {
// 不存在返回-1
return -1;
}
} // 新增键值对
public void put(int key,int value){
Node node = new Node(key, value);
// 首先判断key是否在map中,如果已存在,map直接新增,覆盖旧的value
if (map.containsKey(key)){
// 链表中删除旧的节点,在头部添加新的节点
cache.remove(map.get(key));
cache.addFirst(node);
map.put(key,node); }else{
// 在向LRU添加key时,如果缓存已满,要删除最后一个Node,同时map中也要记得删除该key
if (cache.size()==cap){
Node last = cache.removeLast();
map.remove(last.key);
}
cache.addFirst(node);
map.put(key,node);
} } // 输出缓存中的数据
public void listAll(){
cache.printAll();
} }

测试类

class LRUCacheTest {
private LRUCache lruCache = new LRUCache(4); @Test
public void LRUCacheTest() {
lruCache.put(1, 2);
lruCache.put(2, 3);
lruCache.put(3, 4);
lruCache.put(5, 6);
// 因为cache大小是4,再添加新的数据时,会将最早加入缓存的数据淘汰掉
lruCache.put(6, 67);
// 获取第二次添加的数据,获取后(2,3)会被移动到最前边
lruCache.get(2);
lruCache.listAll();
} }

所以输出结果:

总结

对于LRU算法,他的优势在于对热点数据的查询,使用LRU可以有效提高查询效率,但是对于批量查询历史数据,有可能导致缓存污染,偶然的查询历史数据,会覆盖掉热点数据,导致本次查询不再走缓存。

LRU缓存的实现的更多相关文章

  1. LRU缓存实现(Java)

    LRU Cache的LinkedHashMap实现 LRU Cache的链表+HashMap实现 LinkedHashMap的FIFO实现 调用示例 LRU是Least Recently Used 的 ...

  2. 转: LRU缓存介绍与实现 (Java)

    引子: 我们平时总会有一个电话本记录所有朋友的电话,但是,如果有朋友经常联系,那些朋友的电话号码不用翻电话本我们也能记住,但是,如果长时间没有联系了,要再次联系那位朋友的时候,我们又不得不求助电话本, ...

  3. volley三种基本请求图片的方式与Lru的基本使用:正常的加载+含有Lru缓存的加载+Volley控件networkImageview的使用

    首先做出全局的请求队列 package com.qg.lizhanqi.myvolleydemo; import android.app.Application; import com.android ...

  4. 如何用LinkedHashMap实现LRU缓存算法

    阿里巴巴笔试考到了LRU,一激动忘了怎么回事了..准备不充分啊.. 缓存这个东西就是为了提高运行速度的,由于缓存是在寸土寸金的内存里面,不是在硬盘里面,所以容量是很有限的.LRU这个算法就是把最近一次 ...

  5. 面试挂在了 LRU 缓存算法设计上

    好吧,有人可能觉得我标题党了,但我想告诉你们的是,前阵子面试确实挂在了 RLU 缓存算法的设计上了.当时做题的时候,自己想的太多了,感觉设计一个 LRU(Least recently used) 缓存 ...

  6. Java集合详解5:深入理解LinkedHashMap和LRU缓存

    今天我们来深入探索一下LinkedHashMap的底层原理,并且使用linkedhashmap来实现LRU缓存. 摘要: HashMap和双向链表合二为一即是LinkedHashMap.所谓Linke ...

  7. 04 | 链表(上):如何实现LRU缓存淘汰算法?

    今天我们来聊聊“链表(Linked list)”这个数据结构.学习链表有什么用呢?为了回答这个问题,我们先来讨论一个经典的链表应用场景,那就是+LRU+缓存淘汰算法. 缓存是一种提高数据读取性能的技术 ...

  8. LRU缓存原理

    LRU(Least Recently Used)  LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象. 采用LRU算法的缓存有两种:LrhCache和DisL ...

  9. 链表(上):如何实现LRU缓存淘汰算法?

    一.什么是链表 和数组一样,链表也是一种线性表. 从内存结构来看,链表的内存结构是不连续的内存空间,是将一组零散的内存块串联起来,从而进行数据存储的数据结构. 链表中的每一个内存块被称为节点Node. ...

  10. [Leetcode]146.LRU缓存机制

    Leetcode难题,题目为: 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key ...

随机推荐

  1. 题解-Infinite Path

    题解-Infinite Path \(\color{#9933cc}{\texttt{Infinite Path}}\) \(T\) 组测试数据.每次给你一个 \(n\) 的排列 \(\{p_n\}\ ...

  2. 精尽Spring MVC源码分析 - WebApplicationContext 容器的初始化

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  3. 【Jenkins】环境配置及安装

    下载地址: 国外官网:https://www.jenkins.io/zh/download/(版本最新) 国内镜像:http://mirrors.jenkins-ci.org/windows/ 清华镜 ...

  4. Nosql 和 Sql 简单概念介绍

    Nosql (非关系数据库) 代表数据库:redis / hbase /mongoDB /CouchDB /Neo4J 存储数据使用的是数据结构化存储方法的集合,意味着数据的存储可以是文档.集合.键值 ...

  5. mini-web框架-闭包-总结(5.2.1)

    @ 目录 1.说明 2.代码 关于作者 1.说明 闭包关键: 1.在函数内部再定义函数 2.函数在不调用情况下不执行. 3.和创建对象差不多,但返回的为内部函数引用 2.代码 # y = kx+b d ...

  6. 关于python面试中的设计模式,搞懂这些就足够了

    1.什么是设计模式? 设计模式是经过总结.优化,对我们经常遇到的一些编程问题的可重用的解决方案.设计模式不同于类或库可直接作用于代码.相反,它更为的高级,是一种必须在特定的情形下实现的方法模版. 2. ...

  7. Autofac的基本使用---1、前言

    Autofac的基本使用---目录 代码地址 https://github.com/catbiscuit/AutofacStudy 参考网上的大神,原博文地址 https://www.cnblogs. ...

  8. Excel 多/整列(多/整行)移位操作

    步骤1:创建测试数据 步骤2:把B列和C列进行移位操作(整列移位操作,多列移位操作方法一样) 选中B列,鼠标放到B列边缘地带,直到鼠标显示带有四个箭头方向为止,点击键盘shift键进行拖拽,拖拽时显示 ...

  9. Kibana查询语言(KQL)

    一.前言 现在大多数的公司都会使用ELK组合来对日志数据的收集.存储和提供查询服务,这里就不介绍什么是ELK了,只介绍一些EKL中的查询,也就是K(kibana). 查询数据库,如果是MySQL,那么 ...

  10. 【linux】系统编程-1-进程、管道和信号

    目录 1. 进程 1.1 概念 1.2 查看进程 1.3 启动新进程 1.3.1 system() 函数 1.3.2 fork() 函数 1.3.2 exce 系列函数 1.3.2.1 exce 系列 ...