LinkedHashMap实现LRU算法
2014-04-23 16:07
387 查看
LinkedHashMap特别有意思,它不仅仅是在HashMap上增加Entry的双向链接,它更能借助此特性实现保证Iterator迭代按照插入顺序(以insert模式创建LinkedHashMap)或者实现LRU(LeastRecentlyUsed最近最少算法,以access模式创建LinkedHashMap)。
下面是LinkedHashMap的get方法的代码
[code]Entry<K,V>e=(Entry<K,V>)getEntry(key);
[/code]
其中有一段:e.recordAccess(this)。下面我们进入Entry的定义
[code]LinkedHashMap<K,V>lm=(LinkedHashMap<K,V>)m;
[/code]
这里的addBefore(lm.header)是做什么呢?再看
[code]after=existingEntry;
[/code]
从这里可以看到了,addBefore(lm.header)是把当前访问的元素挪到head的前面,即最近访问的元素被放到了链表头,如此要实现LRU算法只需要从链表末尾往前删除就可以了,多么巧妙的方法。
在看到LinkedHashMap之前,我以为实现LRU算法是在每个元素内部维护一个计数器,访问一次自增一次,计数器最小的会被移除。但是要想到,每次add的时候都需要做这么一次遍历循环,并取出最小的抛弃,在HashMap较大的时候效率很差。当然也有其他方法来改进,比如建立<访问次数,LinkedHashMap元素的key>这样的TreeMap,在add的时候往TreeMap里也插入一份,删除的时候取最小的即可,改进了效率但没有LinkedHashMap内部的默认实现来的简捷。
LinkedHashMap是什么时候删除的呢?
[code]super.addEntry(hash,key,value,bucketIndex);
[/code]
在增加Entry的时候,通过removeEldestEntry(eldest)判断是否需要删除最老的Entry,如果需要则remove。注意看这里Entry<K,V>eldest=header.after,记得我们前面提过LinkedHashMap还维护一个双向链表,这里的header.after就是链表尾部最后一个元素(头部元素是head.before)。
LinkedHashMap默认的removeEldestEntry方法如下
[code]returnfalse;
[/code]
总是返回false,所以开发者需要实现LRU算法只需要继承LinkedHashMap并重写removeEldestEntry方法,下面以MyBatis的LRU算法的实现举例
[code]privatestaticfinallongserialVersionUID=4267176411845948333L;
[/code]
开发者的子类并不需要直接操作eldest(上例中获得eldestKey只是MyBatis需要映射到Cache对象中的元素),只要根据自己的条件(一般是元素个数是否到达阈值)返回true/false即可。注意,要按照LRU排序必须在newLinkedHashMap()的构造函数的最后一个参数传入true(true代表LinkedHashMap内部的双向链表按访问顺序排序,false代表按插入顺序排序)。
在LinkedHashMap的注释里明确提到,该类在保持插入顺序、不想HashMap那样混乱的情况下,又没有像TreeMap那样的性能损耗。同时又能够很巧妙地实现LRU算法。其他方面和HashMap功能一致。有兴趣的同学可以仔细看看LinkedHashMap的实现。
下面是LinkedHashMap的get方法的代码
publicVget(Objectkey){
[code]Entry<K,V>e=(Entry<K,V>)getEntry(key);
if(e==null)
returnnull;
e.recordAccess(this);
returne.value;
}
[/code]
其中有一段:e.recordAccess(this)。下面我们进入Entry的定义
voidrecordAccess(HashMap<K,V>m){
[code]LinkedHashMap<K,V>lm=(LinkedHashMap<K,V>)m;
if(lm.accessOrder){
lm.modCount++;
remove();
addBefore(lm.header);
}
}
[/code]
这里的addBefore(lm.header)是做什么呢?再看
privatevoidaddBefore(Entry<K,V>existingEntry){
[code]after=existingEntry;
before=existingEntry.before;
before.after=this;
after.before=this;
}
[/code]
从这里可以看到了,addBefore(lm.header)是把当前访问的元素挪到head的前面,即最近访问的元素被放到了链表头,如此要实现LRU算法只需要从链表末尾往前删除就可以了,多么巧妙的方法。
在看到LinkedHashMap之前,我以为实现LRU算法是在每个元素内部维护一个计数器,访问一次自增一次,计数器最小的会被移除。但是要想到,每次add的时候都需要做这么一次遍历循环,并取出最小的抛弃,在HashMap较大的时候效率很差。当然也有其他方法来改进,比如建立<访问次数,LinkedHashMap元素的key>这样的TreeMap,在add的时候往TreeMap里也插入一份,删除的时候取最小的即可,改进了效率但没有LinkedHashMap内部的默认实现来的简捷。
LinkedHashMap是什么时候删除的呢?
voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex){
[code]super.addEntry(hash,key,value,bucketIndex);
//Removeeldestentryifinstructed
Entry<K,V>eldest=header.after;
if(removeEldestEntry(eldest)){
removeEntryForKey(eldest.key);
}
}
[/code]
在增加Entry的时候,通过removeEldestEntry(eldest)判断是否需要删除最老的Entry,如果需要则remove。注意看这里Entry<K,V>eldest=header.after,记得我们前面提过LinkedHashMap还维护一个双向链表,这里的header.after就是链表尾部最后一个元素(头部元素是head.before)。
LinkedHashMap默认的removeEldestEntry方法如下
protectedbooleanremoveEldestEntry(Map.Entry<K,V>eldest){
[code]returnfalse;
}
[/code]
keyMap=newLinkedHashMap<Object,Object>(size,.75F,true){
[code]privatestaticfinallongserialVersionUID=4267176411845948333L;
protectedbooleanremoveEldestEntry(Map.Entry<Object,Object>eldest){
booleantooBig=size()>size;
if(tooBig){
eldestKey=eldest.getKey();
}
returntooBig;
}
};
[/code]
开发者的子类并不需要直接操作eldest(上例中获得eldestKey只是MyBatis需要映射到Cache对象中的元素),只要根据自己的条件(一般是元素个数是否到达阈值)返回true/false即可。注意,要按照LRU排序必须在newLinkedHashMap()的构造函数的最后一个参数传入true(true代表LinkedHashMap内部的双向链表按访问顺序排序,false代表按插入顺序排序)。
在LinkedHashMap的注释里明确提到,该类在保持插入顺序、不想HashMap那样混乱的情况下,又没有像TreeMap那样的性能损耗。同时又能够很巧妙地实现LRU算法。其他方面和HashMap功能一致。有兴趣的同学可以仔细看看LinkedHashMap的实现。
相关文章推荐
- Android使用LinkedHashMap实现一个LRU算法的内存缓存
- 用LinkedHashMap实现LRU算法
- LinkedHashMap 和 LRU算法实现
- 使用 Java.util.LinkedHashMap 实现 LRU、FIFO 算法
- LRU算法的实现使用linkedHashMap方法,以及使用双链表+hashtable方法
- 利用LinkedHashMap实现LRU算法缓存
- 基于LinkedHashMap实现LRU缓存调度算法原理
- 使用LinkedHashMap来实现一个使用LRU(Least Recently Used)算法的cache
- 基于LinkedHashMap实现LRU缓存调度算法原理
- 使用 LinkedHashMap 实现 LRU 算法
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
- 用LinkedHashMap实现的LRU算法
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
- 剖析LRU算法及LinkedHashMap源码实现机制
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
- LinkedHashMap的实现原理(LRU算法)
- 基于LRU算法的缓存实现
- 理解LinkedHashMap实现LRU缓存
- 最近最久未使用页面淘汰算法———LRU算法(java实现)
- LRU缓存淘汰算法分析与实现