缓存淘汰算法之LRU实现

时间:2022-11-03 10:58:57

Java中最简单的LRU算法实现,就是利用 LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可

如果你去看LinkedHashMap的源码可知,LRU算法是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入的内容直接放在链表头,
如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置。
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* LinkedHashMap实现简单的缓存, 必须实现removeEldestEntry方法,具体参见JDK文档
* @author
* 2017年9月1日
*/
public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> { private final int maxCapacity;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private final Lock lock = new ReentrantLock(); public LRULinkedHashMap(int maxCapacity) {
super(maxCapacity, DEFAULT_LOAD_FACTOR, true);
this.maxCapacity = maxCapacity;
} @Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
return size() > maxCapacity;
}
@Override
public boolean containsKey(Object key) {
try {
lock.lock();
return super.containsKey(key);
} finally {
lock.unlock();
}
} @Override
public V get(Object key) {
try {
lock.lock();
return super.get(key);
} finally {
lock.unlock();
}
} @Override
public V put(K key, V value) {
try {
lock.lock();
return super.put(key, value);
} finally {
lock.unlock();
}
} public int size() {
try {
lock.lock();
return super.size();
} finally {
lock.unlock();
}
} public void clear() {
try {
lock.lock();
super.clear();
} finally {
lock.unlock();
}
} public Collection<Map.Entry<K, V>> getAll() {
try {
lock.lock();
return new ArrayList<Map.Entry<K, V>>(super.entrySet());
} finally {
lock.unlock();
}
}
}

基于双链表的LRU实现

传统意义的LRU算法是为每一个Cache对象设置一个计数器,每次Cache命中则给计数器+1,而Cache用完,需要淘汰旧内容,放置新内容时,就查看所有的计数器,并将最少使用的内容替换掉。

它的弊端很明显,如果Cache的数量少,问题不会很大, 但是如果Cache的空间过大,达到10W或者100W以上,一旦需要淘汰,则需要遍历所有计算器,其性能与资源消耗是巨大的。效率也就非常的慢了。

它的原理: 将Cache的所有位置都用双连表连接起来,当一个位置被命中之后,就将通过调整链表的指向,将该位置调整到链表头的位置,新加入的Cache直接加到链表头中。

这样,在多次进行Cache操作后,最近被命中的,就会被向链表头方向移动,而没有命中的,而想链表后面移动,链表尾则表示最近最少使用的Cache。

当需要替换内容时候,链表的最后位置就是最少被命中的位置,我们只需要淘汰链表最后的部分即可。

import java.util.Hashtable;

public class LRUCache {  

    class CacheNode {
CacheNode prev;//前一节点
CacheNode next;//后一节点
Object value;//值
Object key;//键
CacheNode() {
}
} private int cacheSize;
private Hashtable nodes;//缓存容器
private int currentSize;
private CacheNode first;//链表头
private CacheNode last;//链表尾 public LRUCache(int i) {
currentSize = 0;
cacheSize = i;
nodes = new Hashtable(i);//缓存容器
} /**
* 获取缓存中对象
* @param key
* @return
*/
public Object get(Object key) {
CacheNode node = (CacheNode) nodes.get(key);
if (node != null) {
moveToHead(node);
return node.value;
} else {
return null;
}
} /**
* 添加缓存
* @param key
* @param value
*/
public void put(Object key, Object value) {
CacheNode node = (CacheNode) nodes.get(key); if (node == null) {
//缓存容器是否已经超过大小.
if (currentSize >= cacheSize) {
if (last != null)//将最少使用的删除
nodes.remove(last.key);
removeLast();
} else {
currentSize++;
} node = new CacheNode();
}
node.value = value;
node.key = key;
//将最新使用的节点放到链表头,表示最新使用的.
moveToHead(node);
nodes.put(key, node);
}
/**
* 将缓存删除
* @param key
* @return
*/
public Object remove(Object key) {
CacheNode node = (CacheNode) nodes.get(key);
if (node != null) {
if (node.prev != null) {
node.prev.next = node.next;
}
if (node.next != null) {
node.next.prev = node.prev;
}
if (last == node)
last = node.prev;
if (first == node)
first = node.next;
}
return node;
}
public void clear() {
first = null;
last = null;
}
/**
* 删除链表尾部节点
* 表示 删除最少使用的缓存对象
*/
private void removeLast() {
//链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)
if (last != null) {
if (last.prev != null)
last.prev.next = null;
else
first = null;
last = last.prev;
}
} /**
* 移动到链表头,表示这个节点是最新使用过的
* @param node
*/
private void moveToHead(CacheNode node) {
if (node == first)
return;
if (node.prev != null)
node.prev.next = node.next;
if (node.next != null)
node.next.prev = node.prev;
if (last == node)
last = node.prev;
if (first != null) {
node.next = first;
first.prev = node;
}
first = node;
node.prev = null;
if (last == null)
last = first;
} }

缓存淘汰算法之LRU实现的更多相关文章

  1. 两种缓存淘汰算法LFU&amp&semi;LRU

    LRU全称是Least Recently Used,即最近最久未使用的意思. LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小.也就是说,当限定的空间已 ...

  2. 缓存淘汰算法之LRU

    1. LRU1.1. 原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”. ...

  3. 两种常见的缓存淘汰算法LFU&amp&semi;LRU

    1. LFU 1.1. 原理 LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”. 1.2.  ...

  4. 04 &vert; 链表(上):如何实现LRU缓存淘汰算法&quest;

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

  5. 数据结构与算法之美 06 &vert; 链表&lpar;上&rpar;-如何实现LRU缓存淘汰算法

    常见的缓存淘汰策略: 先进先出 FIFO 最少使用LFU(Least Frequently Used) 最近最少使用 LRU(Least Recently Used) 链表定义: 链表也是线性表的一种 ...

  6. 链表:如何实现LRU缓存淘汰算法&quest;

    缓存淘汰策略: FIFO:先入先出策略 LFU:最少使用策略 LRU:最近最少使用策略   链表的数据结构: 可以看到,数组需要连续的内存空间,当内存空间充足但不连续时,也会申请失败触发GC,链表则可 ...

  7. 聊聊缓存淘汰算法-LRU 实现原理

    前言 我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来.缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据.常用淘 ...

  8. 《数据结构与算法之美》 &lt&semi;04&gt&semi;链表(上):如何实现LRU缓存淘汰算法&quest;

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

  9. 图解缓存淘汰算法二之LFU

    1.概念分析 LFU(Least Frequently Used)即最近最不常用.从名字上来分析,这是一个基于访问频率的算法.与LRU不同,LRU是基于时间的,会将时间上最不常访问的数据淘汰;LFU为 ...

随机推荐

  1. Python基础(二)

    本章内容: Python 运算符(算术运算.比较运算.赋值运算.逻辑运算.成员运算) 基本数据类型(数字.布尔值.字符串.列表.元组.字典.set集合) for 循环 enumrate range和x ...

  2. windows系统命令总结

    windows系统命令总结 IIS管理器:inetmgr SQL server数据库管理器:ssms windows服务:services.msc

  3. android常见错误-The container &&num;39&semi;Android Dependencies&&num;39&semi; references non existing library

    The container 'Android Dependencies' references non existing library

  4. 浏览器中 for in 反射 对象成员 的差异

    http://www.cnblogs.com/_franky/archive/2010/05/08/1730437.html 下面是例子 function test(url, obj) { if($( ...

  5. mybatisnet轻量级ORM框架

    https://code.google.com/p/mybatisnet/source/checkout http://blog.csdn.net/arvinstudy/article/details ...

  6. 在maven仓库中查找jar

    findmaven.net是一个查找Jar和查找Maven的Maven仓库搜索引擎,它能够依据Java开发人员提供的Class名或者Jar名找到包括它的Jar.同一时候提供Jar的Maven仓库链接. ...

  7. DevExpress内 GridControl中复选框值问题

    在DevExpress的 GridControl内的复选柜勾选后,界面看到是勾选状态,但对应的DataView的值仍未变,在以下事件内处理 在对应的DataView内的 CellValueChangi ...

  8. oracle12C安装步骤

    首先去官网下载两个架包链接如下:官网链接 第一步:将两个架包解压到同一个database目录下.如截图所示: 第二步:打开setup应用程序 打开后就到了下面这个页面 第三步:配置安全更新 环境变量配 ...

  9. shell的while和until 的用法

    shell while循环工作中使用的不多,一般适用于守护进程程序或始终循环执行场景,其他循环计算等. while条件句: 语法: while 条件 do 指令… done ok,我们测试一下: 测试 ...

  10. tomcat启动出现Preparing launch delegate,一直卡在100&percnt;

    本地启动项目时,Tomcat一直停留在, Starting Tomcat V8.0 Server at localhost   Preparing launch delegate...    百度可得 ...

相关文章