缓存这个东西就是为了提高运行速度的,由于缓存是在寸土寸金的内存里面,不是在硬盘里面,所以容量是很有限的。

LRU这个算法就是把最近一次使用时间离现在时间最远的数据删除掉。

先说说List:每次访问一个元素后把这个元素放在 List一端,这样一来最远使用的元素自然就被放到List的另一端。缓存满了t的时候就把那最远使用的元素remove掉。但更实用的是 HashMap。因为List太慢,要删掉的数据总是位于List底层数组的第一个位置,删掉之后,后面的数据要向前补位。。所以复杂度是O(n),那就用链表结构的LinkedHashMap呗~,LinkedHashMap默认的元素顺序是put的顺序,但是如果使用带参数的构造函数,那么 LinkedHashMap会根据访问顺序来调整内部 顺序。

LinkedHashMap的get()方法除了返回元素之外还可以把被访问的元素放到链表的底端,这样一来每次顶端的元素就是remove的元素。

构造函数如下:

public LinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder);
  • 1
  • 2

initialCapacity 初始容量

loadFactor 加载因子,一般是 0.75f

accessOrder false 基于插入顺序 true 基于访问顺序(get一个元素后,这个元素被加到最后,使用了LRU 最近最少被使用的调度算法)

看一下LinkedHashMap的相关实现源码吧:

// 双向链表的 head节点
private transient Entry<K,V> header;// 访问顺序 true:访问顺序;false:插入顺序
private final boolean accessOrder;// get方法 每个get之后,都要进行recordAccess操作
public V get(Object key) {Entry<K,V> e = (Entry<K,V>)getEntry(key);if (e == null)return null;e.recordAccess(this);return e.value;
}// recordAccess 方法中,先remove该节点,再在head之间添加节点,表示最近访问了
void recordAccess(HashMap<K,V> m) {LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;if (lm.accessOrder) {lm.modCount++;remove();addBefore(lm.header);}
}// 删除该节点
private void remove() {before.after = after;after.before = before;
}private void addBefore(Entry<K,V> existingEntry) {after  = existingEntry;before = existingEntry.before;before.after = this;after.before = this;
}// 添加新的entry时,如果需要删除eldest节点,就会删除head.after,如果是访问顺序,也就是最早以前访问的节点
void addEntry(int hash, K key, V value, int bucketIndex) {super.addEntry(hash, key, value, bucketIndex);// Remove eldest entry if instructedEntry<K,V> eldest = header.after;if (removeEldestEntry(eldest)) {removeEntryForKey(eldest.key);}
}   // 默认的removeEldestEntry方法是返回false的,也就是不会进行删除,而是进行扩容protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false;
}
这是实现LRU的关键,我们可以重写这个方法,让其删除eldest entry;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

来个例子吧: 
import java.util.*;

class Test
{  public static void main(String[] args) throws Exception{  Map<Integer,Integer> map=new LinkedHashMap<>(10,0.75f,true);  map.put(9,3);  map.put(7,4);  map.put(5,9);  map.put(3,4);  //现在遍历的话顺序肯定是9,7,5,3  //下面访问了一下9,3这个键值对,输出顺序就变喽~  map.get(9);  for(Iterator<Map.Entry<Integer,Integer>> it=map.entrySet().iterator();it.hasNext();){  System.out.println(it.next().getKey());  }  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

输出 



9

好玩吧~ 
下面开始实现LRU缓存喽~

import java.util.*;
//扩展一下LinkedHashMap这个类,让他实现LRU算法
class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{  //定义缓存的容量  private int capacity;  private static final long serialVersionUID = 1L;  //带参数的构造器     LRULinkedHashMap(int capacity){  //调用LinkedHashMap的构造器,传入以下参数  super(16,0.75f,true);  //传入指定的缓存最大容量  this.capacity=capacity;  }  //实现LRU的关键方法,如果map里面的元素个数大于了缓存最大容量,则删除链表的顶端元素  @Override  public boolean removeEldestEntry(Map.Entry<K, V> eldest){   System.out.println(eldest.getKey() + "=" + eldest.getValue());    return size()>capacity;  }
}
//测试类
class Test{
public static void main(String[] args) throws Exception{  //指定缓存最大容量为4  Map<Integer,Integer> map=new LRULinkedHashMap<>(4);  map.put(9,3);  map.put(7,4);  map.put(5,9);  map.put(3,4);  map.put(6,6);  //总共put了5个元素,超过了指定的缓存最大容量  //遍历结果  for(Iterator<Map.Entry<Integer,Integer>> it=map.entrySet().iterator();it.hasNext();){  System.out.println(it.next().getKey());  }  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

输出结果如下 
9=3 
9=3 
9=3 
9=3 
9=3 


3

分析一下:使用带参数构造器,且启用LRU模式的LinkedHashMap会在每次有新元素加入的时候,判断当前储存元素是否超过了缓存上限,也就是执行 
一次removeEldestEntry方法,看最后的遍历结果,发现果然把9删除了,LRU发挥作用了~

LinkedHashMap实现LRU缓存算法相关推荐

  1. 实现 LRU 缓存算法

    1 LRU 缓存介绍 LRU 算法全称是最近最少使用算法(Least Recently Use),是一种简单的缓存策略.顾名思义,LRU 算法会选出最近最少使用的数据进行淘汰. 那么什么是缓存呢?缓存 ...

  2. 保研面试 算法题_面试挂在了一道 LRU 缓存算法设计题

    前言 好吧,有人可能觉得我标题党了,但我想告诉你们的是,前阵子面试确实挂在了 RLU 缓存算法的设计上了. 当时面试官问到这个题的时候,很快给了一个思路,但是手写的时候,发现自己没有深刻理解它,加上当 ...

  3. 【文科生带你读JavaScript数据结构与算法】2. 双向链表与LRU缓存算法原理与实现(下)

    上篇聊了聊双向链表(doubly linked list)这个数据结构,今天就来点更实际的,也可以用双链表来实现的一种生活工作中不可或缺的优化算法--LRU缓存(Least Recently Used ...

  4. java mysql lru_Java集合详解5:深入理解LinkedHashMap和LRU缓存

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

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

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  6. LRU缓存算法缓存设计和实现

    什么是缓存? 举个例子,去图书馆查资料,一般情况下我们会集中把我们有可能查阅的几本书从书架取下来,放在我们的桌面上,以便交叉查阅,从而避免频繁的从座位上跑到书架旁去取书.在这个例子里,书桌所扮演的就是 ...

  7. JDK1.8源码分析:LinkedHashMap与LRU缓存设计思路

    概述 LinkedHashMap继承于HashMap,在HashMap的基础上,新增了两个特性: 支持以节点的插入顺序来迭代该map内的所有节点: 支持缓存设计中LRU的特性,即LinkedHashM ...

  8. 算法必知 --- LRU缓存淘汰算法

    作者:_code_x 链接:https://www.jianshu.com/p/b7fed77324b9 写在前 就是一种缓存淘汰策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾 ...

  9. LRU缓存实现(Java)

    LRU是Least Recently Used 的缩写,翻译过来就是"最近最少使用",LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的 ...

最新文章

  1. javascript-对混合字母/数字数组进行排序
  2. Windows 家族的十二种常用密码破解法
  3. Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)
  4. windows10 ipv4设置两个(多个)网段同时连接(多网段、双网段)
  5. python 列表创建
  6. github搭建个人博客 hexo d无效
  7. 在容器内源码安装PostgreSQL13.2
  8. mysql分裂函数函数_分裂函数等价于T-SQL?
  9. Spring的三种注入方式
  10. live2d_基于Qt的Live2D示例(几乎都是官方给的代码就是啦!)
  11. 企业文化是数字化转型最大障碍-解读《2022年首席数据官调查报告》
  12. 海贼oj#130. 计算复利2
  13. vue导出excel加一个进度条_vue项目中如何把数据导出成excel文件
  14. windows添加右键打开cmd命令窗口
  15. terracotta安装配置与集群过程
  16. PHP语言编程魔方,php魔方方法总结
  17. dmz主机就是DNAT功能的体现
  18. word标尺灰色_如何在Microsoft Word中显示和隐藏标尺
  19. 软件单元测试操作步骤(java版)
  20. 寒假社会实践完整版内容,一套流程

热门文章

  1. c语言中常用的预处理命令6,C语言的预处理命令
  2. Linux配置最基础的命令
  3. 各大型邮箱smtp服务器及端口收集:
  4. docker 进入容器命令_Docker容器常用命令分享,赶紧收藏
  5. 2021年东港二中高考成绩查询,辽宁省东港二中2021届英语高考模拟试卷1(新课改原创2020版,供2021年课改省份考生使用)...
  6. android 获取位置数据库,尝试从webview获取位置时,Android“SQLite数据库无法从/CachedGeoposition.db加载”错误...
  7. gridreport如何设置打印3次_如何设置光固化3D打印机切片参数
  8. 通讯录排序 (20分)_算法入门篇:简单的排序算法
  9. python如何实现matlab_Python实现matlab数据绘制
  10. visio保存后公式变形_涨姿势了!仿真变形后的模型还能保存下来