Fork me on GitHub

借助LinkedHashMap实现基于LRU算法缓存

一、LRU算法介绍

LRU(Least Recently Used)最近最少使用算法,是用在操作系统中的页面置换算法,因为内存空间是有限的,不可能把所有东西都放进来,所以就必须要有所取舍,我们应该把什么东西放进来呢?有没有什么判定标准呢?页面置换算法就是干这个的,企图通过之前的行为预测到之后的行为(这是概率问题),而LRU就是其中的一种,它的基本思想就是既然有一块数据,最近的一段时间内它是最少访问的,这说明在这之后它也可能是最少访问的,如果非要移除一个的话,我只好把它置换出内存了。

 

总结一下:

    LRU:在空间有限的情况下必须做出取舍,将最可能最少被访问的数据置换掉。

 

 二、为什么使用LRU

QUESTION: 我用HashMap也挺好的啊,为什么要使用缓存昂?!

用数据说话,做一个实验来说明为什么要使用缓存,new两个Map底层分别使用HashMap和LinkedHashMap,然后往里面装一百万条数据,然后分别总是访问同一条数据十万次和随机访问十万次:

 1 import java.util.HashMap;
 2 import java.util.LinkedHashMap;
 3 import java.util.Map;
 4 import java.util.Random;
 5 
 6 public class Main {
 7 
 8     public static void main(String[] args) {
 9         
10         Map<Integer,Integer> map1=new LinkedHashMap<Integer,Integer>(16,0.75F,true);
11         Map<Integer,Integer> map2=new HashMap<Integer,Integer>();
12         
13         for(int i=0;i<1000000;i++){
14             map1.put(i,i);
15             map2.put(i,i);
16         }
17         
18         // 大量访问同一条数据
19         long start=System.currentTimeMillis();
20         for(int i=0;i<1000000000;i++){
21             map1.get(1000000);
22         }
23         System.out.printf("HashMap: %dms\n",System.currentTimeMillis()-start);
24         
25         start=System.currentTimeMillis();
26         for(int i=0;i<100000;i++){
27             map2.get(999);
28         }
29         System.out.printf("LinkedHashMap: %dms\n",System.currentTimeMillis()-start);
30         
31         
32         //随机访问数据
33         start=System.currentTimeMillis();
34         for(int i=0;i<1000000000;i++){
35             map1.get(1000000);
36         }
37         System.out.printf("HashMap: %dms\n",System.currentTimeMillis()-start);
38         
39         start=System.currentTimeMillis();
40         for(int i=0;i<100000;i++){
41             map2.get(new Random().nextInt(1000000));
42         }
43         System.out.printf("LinkedHashMap: %dms\n",System.currentTimeMillis()-start);
44         
45     }
46     
47 }

结果如下:

我们打一个表格以便于分析:

首先进行横向比较,无论怎么访问LinkedHashMap总是要比HashMap快得多,

然后进行纵向比较发现HashMap访问同一条数据和随机访问花费的时间差不多,因为它是无状态的,也就是它并不能根据之前的结果帮助到以后的查询,LinkedHashMap的随机访问明显要慢一些了,LRU的策略主要就是用于短时间内频繁的访问相同的数据的。

 

三、如何使用LinkedHashMap实现LRUMap

继承LinkedHashMap然后覆写removeEldestEntry方法即可,这个方法在每次添加元素之前调用来判断是否需要删除元素,如果需要的话就会用LRU算法移除掉最近最少使用的元素,如下:

 1 import java.util.Iterator;
 2 import java.util.LinkedHashMap;
 3 import java.util.Map;
 4 import java.util.Map.Entry;
 5 
 6 public class Main {
 7 
 8     public static void main(String[] args) {
 9         
10         Map<Integer,Integer> map=new LRUMap<>(3);
11         
12         map.put(2,2);
13         map.put(3,3);
14         map.put(5,5);
15         map.get(2);
16         map.put(7,7);
17         map.put(11,11);
18         
19         for(Iterator<Entry<Integer,Integer>> iter=map.entrySet().iterator();iter.hasNext();){
20             System.out.print(iter.next().getKey()+" ");  //2 7 11
21         }
22         
23     }
24     
25 }
26 
27 class LRUMap<K,V> extends LinkedHashMap<K,V>{
28     
29     private int capacity;
30     
31     public LRUMap() {
32         this(16);
33     }
34     
35     public LRUMap(int capacity) {
36         super(capacity,0.75F,true);
37         this.capacity=capacity;
38     }
39     
40     @Override
41     protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
42         return size()>capacity;
43     }
44     
45 }

内存情况如下(最左端为最常访问的):

申请空间:

 map.put(2,2);

 map.put(3,3);

map.put(5,5);

map.get(2);

map.put(7,7);

map.put(11,11);

所以最后打印出来的就是2 7 11。

 

posted @ 2016-08-20 21:17  CC11001100  阅读(470)  评论(0编辑  收藏  举报