LinkedHashMap 的理解以及借助其实现LRU

LinkedHashMap中有一个参数 accessOrder,这个参数定义了LinkedHashMap的访问顺序。

LinkedHashMap中继承了Node,给Node新增了2个新的属性before和after

static class Entry<K,V> extends HashMap.Node<K,V> {Entry<K,V> before, after;Entry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}
}

put方法

put方法LinkedHashMap没有重写,使用的是HashMap的put,但是其中还是有不同,重写了其中几个方法,先看代码:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)//重写newNode方法tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {//重写newNode方法p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;//重写afterNodeAccessafterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();//重写afterNodeInsertionafterNodeInsertion(evict);return null;
}

看下重写的这几个方法:

newNode方法:

实现了每调用一次newNode方法,利用before和after节点可把新节点插入到队尾。保证了一种调用newNode的顺序。

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {LinkedHashMap.Entry<K,V> p =new LinkedHashMap.Entry<K,V>(hash, key, value, e);linkNodeLast(p);return p;
}private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {LinkedHashMap.Entry<K,V> last = tail;tail = p;if (last == null)head = p;else {p.before = last;last.after = p;}
}

afterNodeAccess方法:

在put一个已存在的key时且 accessOrder 为true 时会调用,此方法会这个节点放到队伍的最后。

void afterNodeAccess(Node<K,V> e) { // move node to lastLinkedHashMap.Entry<K,V> last;if (accessOrder && (last = tail) != e) {LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;p.after = null;if (b == null)head = a;elseb.after = a;if (a != null)a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}tail = p;++modCount;}
}

afterNodeInsertion方法

有新的Node时会调用,默认removeEldestEntry返回false,所以这一个方法什么都不做。不过可以看下如果返回不是false,则会删除head节点。这也是实现LRU的一个基础。

void afterNodeInsertion(boolean evict) { // possibly remove eldestLinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {K key = first.key;removeNode(hash(key), key, null, false, true);}
}protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false;
}

get方法

get方法 会在accessOrder为true时把这个节点放到以before ,after为基础的一个双向链表的队尾。

public V get(Object key) {Node<K,V> e;if ((e = getNode(hash(key), key)) == null)return null;if (accessOrder)afterNodeAccess(e);return e.value;
}

foreach,keySet,entrySet遍历方法

所有的遍历在LinkedHashMap里都会重写,都是按照以before ,after为基础的一个双向链表的顺序进行遍历。

accessOrder变量

从上面的get和put过程可以发现,access为false时,则双向链表会变成一个按照插入顺序的链表。如果为true,则会变成按照访问顺序的一个链表。

如何借助LinkedHashMap来实现LRU

上面看到有一个方法removeEldestEntry,默认返回false,这个方法,顾名思义就是删除双向链表最开头的头部节点。

所以要实现LRU,只需要这样即可:

传入accessOrder为true,重写removeEldestEntry,根据缓存目标设置的size来决定在达到多少时来进行remove。

class LRU<K,V> extends LinkedHashMap<K, V>{public LRU(int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity,loadFactor,accessOrder);}@Overrideprotected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {if (this.size()>4) {return true;}return super.removeEldestEntry(eldest);}}

LinkedHashMap 的理解以及借助其实现LRU相关推荐

  1. 【大话Java面试】-如何通俗易懂的理解Redis的回收算法LRU?

    如何通俗易懂的理解LRU算法? 1.LRU是什么? LRU全称Least Recently Used,也就是最近最少使用的意思,是一种内存管理算法,最早应用于Linux操作系统. LRU算法基于一种假 ...

  2. 看动画理解「链表」实现LRU缓存淘汰算法

    前几节学习了「链表」.「时间与空间复杂度」的概念,本节将结合「循环链表」.「双向链表」与 「用空间换时间的设计思想」来设计一个很有意思的缓存淘汰策略:LRU缓存淘汰算法. 循环链表的概念 如上图所示: ...

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

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

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

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

  5. 彻底理解HashMap及LinkedHashMap

    欢迎关注方志朋的博客,回复"666"获面试宝典 来源:https://blog.csdn.net/fuzhongmin05/article/details/104355841 Ha ...

  6. LinkedHashMap实现LRU缓存算法

    缓存这个东西就是为了提高运行速度的,由于缓存是在寸土寸金的内存里面,不是在硬盘里面,所以容量是很有限的. LRU这个算法就是把最近一次使用时间离现在时间最远的数据删除掉. 先说说List:每次访问一个 ...

  7. LinkedHashMap实现LRU算法

    LinkedHashMap 概述 笔者曾提到,HashMap 是 Java Collection Framework 的重要成员,也是Map族(如下图所示)中我们最为常用的一种.不过遗憾的是,Hash ...

  8. java lru lfu_Java集合之LinkedHashMap实现LRU,LFU,FIFO算法

    LinkedHashMap=双向链表+HashMap,存储相比HashMap会多了一个前节点,后节点. LinkedHashMap简介 LinkedHashMap主要是通过HashMap+双向链表来实 ...

  9. 面试官:说说Innodb中LRU怎么做的?

    引言 某日,小编去面试(纯属瞎编),有了如下对话 面试官:"懂mysql吧,知道CPU在读硬盘上数据的时候,是怎么解决CPU和硬盘速度不一致问题么?"我:"懂啊,mysq ...

最新文章

  1. 清理mysql创建的游戏_Linux定时清理游戏log及mysql定时任务删除游戏日志数据的步骤...
  2. 怎样使 Python 输出时不换行?
  3. 【杂谈】如何使用有三AI生态学习计算机视觉和自然语言处理等内容
  4. 记录解决二次编码问题
  5. 韩研究人员声称:创造出了一块“不可破坏”的芯片!
  6. Docker-compose配置Mysql,Redis,MongoDB
  7. java hive demo_java 操作hive通过jdbc
  8. GO国内镜像加速模块下载
  9. 面向对象设计原则之5-接口隔离原则
  10. Android热修复Java类_Android 热修复(一)
  11. python中try怎么用_python下try
  12. 正确使用日志的10个技巧(转)
  13. Android知识散点
  14. 网络安全[脚本小子] -- SSI注入
  15. 基金经理的13年期货感悟(一)
  16. 格林尼治时间与本地时间转换
  17. r76800h怎么样r7 6800h参数
  18. 远程办公:通过cpolar内网穿透,远程桌面控制家里公司内网电脑
  19. 网络系统管理技能大赛知识点一
  20. 你有试过AutoCAD的超级填充功能吗?

热门文章

  1. 机器人简化图画手绘图_高通推出全新RB3 机器人平台,年内支持5G连接
  2. java程序阅读技巧_Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!...
  3. android 内存占用大 卡顿,安卓手机用久了就会卡顿?那是内存使用率高了,你需要这么做...
  4. js调用c语言程序设计,HTML页面,测试JS对C函数的调用简单实例
  5. mint java_Linux Mint19安装jdk1.8.0.191过程
  6. 五十九、如何求N个数的最大公约数和最小公倍数
  7. torch中的topk()函数
  8. 3天造了一个深度学习轮子,生猛!
  9. COLING 2020 | CharBERT:字符敏感的预训练语言模型
  10. 用少于10行代码训练前沿深度学习新药研发模型