java有lrumap吗_LRUMap 源代码实现解读

本文详细解读了Apache Commons Collections的LRUMap,一个基于LRU算法的Map实现。LRUMap在元素超出预设数量时会淘汰最近最少使用的项。文章介绍了LRU算法原理,LRUMap的使用方法,以及get和put操作如何影响元素的最近使用状态。此外,还展示了LRUMap的简单示例及其源码解析,包括get和put操作的关键代码,解释了如何维护元素的链接顺序和淘汰策略。
摘要由CSDN通过智能技术生成

本文通过对Apache Commons Collections项目中LRUMap这个集合类的源代码进行详细解读,为帮助大家更好的了解这个集合类的实现原理以及使用如何该集合类。

首先介绍一下LRU算法. LRU是由Least Recently Used的首字母组成,表示最近最少使用的含义,一般使用在对象淘汰算法上。也是比较常见的一种淘汰算法。

LRUMap 则是实现的LRP算法的Map集合类,它继承于AbstractLinkedMap 抽象类。LRUMap的使用说明如下:

LRUMap的初始化时需要指定最大集合元素个数,新增的元素个数大于允许的最大集合个数时,则会执行LRU淘汰算法。所有的元素在LRUMap中会根据最近使用情况进行排序。最近使用的会放在元素的最前面(LRUMap是通过链表来存储元素内容). 所以LRUMap进行淘汰时只需要删除链表最后一个即可(即header.after所指的元素对象)

那么那些操作会影响元素的使用情况:

1.put 当新增加一个集合元素对象,则表示该对象是最近被访问的

2.get 操作会把当前访问的元素对象作为最近被访问的,会被移到链接表头

注:当执行containsKey和containsValue操作时,不会影响元素的访问情况。

LRUMap也是非线程安全。在多线程下使用可通过 Collections.synchronizedMap(Map)操作来保证线程安全。

LRUMap的一个简单使用示例:

public static voidmain(String[] args) {

LRUMap lruMap =newLRUMap(2);

lruMap.put("a1","1");

lruMap.put("a2","2");

lruMap.get("a1");//mark as recent used

lruMap.put("a3","3");

System.out.println(lruMap);

}

上面的示例,当增加”a3”值时,会淘汰最近最少使用的”a2”, 最后输出的结果为:

{a1=1, a3=3}

下面根据LRUMap的源码来解读一下LRUMap的实现原理

整体类图

4c491334d29c4a9fa9e8eecbe8b36ec9.png

LRUMap类的关键代码说明如下:

1.get操作

publicObject get(Object key) {

LinkEntry entry = (LinkEntry) getEntry(key);

if(entry ==null) {

return null;

}

moveToMRU(entry); //执行LRU操作

returnentry.getValue();

}

moveToMRU方法代码如下:

protected voidmoveToMRU(LinkEntry entry) {

if(entry.after!=header) {

modCount++;

// remove从链接中移除当前节点

entry.before.after= entry.after;

entry.after.before= entry.before;

// add first把节点增加到链接头部

entry.after=header;

entry.before=header.before;

header.before.after= entry;

header.before= entry;

}else if(entry ==header) {

throw newIllegalStateException("Can't move header to MRU"+

" (please report this to commons-dev@jakarta.apache.org)");

}

}

2.put新增操作

由于继承的AbstractLinkedMap,所以put操作会调用addMapping 方法,完整代码如下:

protected voidaddMapping(inthashIndex,inthashCode, Object key, Object value) {

if(isFull()) {

LinkEntry reuse =header.after;

booleanremoveLRUEntry =false;

if(scanUntilRemovable) {

while(reuse !=header&& reuse !=null) {

if(removeLRU(reuse)) {

removeLRUEntry =true;

break;

}

reuse = reuse.after;

}

if(reuse ==null) {

throw newIllegalStateException(

"Entry.after=null, header.after"+header.after+" header.before"+header.before+

" key="+ key +" value="+ value +" size="+size+" maxSize="+maxSize+

" Please check that your keys are immutable, and that you have used synchronization properly."+

" If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

}

}else{

removeLRUEntry = removeLRU(reuse);

}

if(removeLRUEntry) {

if(reuse ==null) {

throw newIllegalStateException(

"reuse=null, header.after="+header.after+" header.before"+header.before+

" key="+ key +" value="+ value +" size="+size+" maxSize="+maxSize+

" Please check that your keys are immutable, and that you have used synchronization properly."+

" If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

}

reuseMapping(reuse, hashIndex, hashCode, key, value);

}else{

super.addMapping(hashIndex, hashCode, key, value);

}

}else{

super.addMapping(hashIndex, hashCode, key, value);

}

}

当集合的个数超过指定的最大个数时,会调用 reuseMapping函数,把要删除的对象的key和value更新为新加入的值,并再次加入到链接表中,并重新排定次序。

reuseMapping函数代码如下:

protected voidreuseMapping(LinkEntry entry,inthashIndex,inthashCode, Object key, Object value) {

// find the entry before the entry specified in the hash table

// remember that the parameters (except the first) refer to the new entry,

// not the old one

try{

intremoveIndex = hashIndex(entry.hashCode,data.length);

HashEntry[] tmp =data;// may protect against some sync issues

HashEntry loop = tmp[removeIndex];

HashEntry previous =null;

while(loop != entry && loop !=null) {

previous = loop;

loop = loop.next;

}

if(loop ==null) {

throw newIllegalStateException(

"Entry.next=null, data[removeIndex]="+data[removeIndex] +" previous="+ previous +

" key="+ key +" value="+ value +" size="+size+" maxSize="+maxSize+

" Please check that your keys are immutable, and that you have used synchronization properly."+

" If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

}

// reuse the entry

modCount++;

removeEntry(entry, removeIndex, previous);

reuseEntry(entry, hashIndex, hashCode, key, value);

addEntry(entry, hashIndex);

}catch(NullPointerException ex) {

throw newIllegalStateException(

"NPE, entry="+ entry +" entryIsHeader="+ (entry==header) +

" key="+ key +" value="+ value +" size="+size+" maxSize="+maxSize+

" Please check that your keys are immutable, and that you have used synchronization properly."+

" If so, then please report this to commons-dev@jakarta.apache.org as a bug.");

}

}

上面的代码中:

removeEntry方法是删除最近最少使用的节点在链接中的引用

reuseEntry方法则把该节点的key和value赋新加入的节点的key和value值

addEntry方法则把该节点加入到链接表,并保障相关的链接顺序

/**

* Adds an entry into this map, maintaining insertion order.

*

* This implementation adds the entry to the data storage table and

* to the end of the linked list.

*

*@paramentry the entry to add

*@paramhashIndex the index into the data array to store at

*/

protected voidaddEntry(HashEntry entry,inthashIndex) {

LinkEntry link = (LinkEntry) entry;

link.after = header;

link.before = header.before;

header.before.after = link;

header.before = link;

data[hashIndex] = entry;

}

LRUMap的主要源码实现就解读到这里,实现思路还是比较好理解的。

LRUMap的使用场景会比较多,例如可以很方便的帮我们实现基于内存的 LRU 缓存服务实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值