java LRU算法介绍和举例

1.前言

    在用户使用联网的软件的时候,总会从网络上获取数据,当在一段时间内要多次使用同一个数据的时候,用户不可能每次用的时候都去联网进行请求,既浪费时间又浪费网络
这时就可以将用户请求过的数据进行保存,但不是任意数据都进行保存,这样会造成内存浪费的。LRU算法的思想就可以运用了。
2.LRU简介
    LRU是Least Recently Used 近期最少使用算法,它就可以将长时间没有被利用的数据进行删除。
    LRU在人们一些情感中也体现的得很好的。当你和一群朋友接触的时候,经常联系的人关系是很好的,若很久没有联系到最后估计都不会再有联系了,也就会是失去
这位朋友了。
3.下面通过代码展现LRU算法:
  •   最简单的一种方法是利用LinkHashMap,因为它本身就有一个方法就是在所设置的缓存范围内,去除掉额外的旧数据
 1 public class LRUByHashMap<K, V> {
 2  /*
 3   * 通过LinkHashMap简单实现LRU算法
 4   */
 5  /**
 6   * 缓存大小
 7   */
 8  private int cacheSize;
 9  /**
10   * 当前缓存数目
11   */
12  private int currentSize;
13  
14  private LinkedHashMap<K, V> maps;
15  
16  public LRUByHashMap(int cacheSize1) {
17   this.cacheSize = cacheSize1;
18  
19   maps = new LinkedHashMap<K, V>() {
20    /**
21     *
22     */
23    private static final long serialVersionUID = 1;
24  
25    // 这里移除旧的缓存数据
26    @Override
27    protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
28  
29     // 当超过缓存数量的时候就将旧的数据移除
30     return getCurrentSize() > LRUByHashMap.this.cacheSize;
31    }
32   };
33  }
34  
35  public synchronized int getCurrentSize() {
36   return maps.size();
37  }
38  
39  public synchronized void put(K k, V v) {
40  
41   if (k == null) {
42    throw new Error("存入的键值不能为空");
43   }
44  
45   maps.put(k, v);
46  }
47  
48  public synchronized void remove(K k) {
49   if (k == null) {
50    throw new Error("移除的键值不能为空");
51   }
52   maps.remove(k);
53  }
54  
55  public synchronized void clear() {
56   maps = null;
57  }
58  
59  // 获取集合
60  public synchronized Collection<V> getCollection() {
61  
62   if (maps != null) {
63    return maps.values();
64   } else {
65    return null;
66   }
67  
68  }
69  
70  public static void main(String[] args) {
71  
72   // 测试
73   LRUByHashMap<Integer, String> maps = new LRUByHashMap<Integer, String>(
74     3);
75   maps.put(1, "1");
76   maps.put(2, "2");
77   maps.put(3, "3");
78   maps.put(4, "4");
79   maps.put(5, "5");
80   maps.put(6, "6");
81   Collection<String> col = maps.getCollection();
82   System.out.println("存入缓存中的数据是--->>" + col.toString());
83  
84  }
85 }
运行后的效果:
 
   
 
代码明明是put了6个Entry但最后只显示了三个,之间的三个是旧的所以直接被咔嚓掉了
  • 第二种方法是利用双向链表 + HashTable
    双向链表的作用是用来记录位置的,而HashTable作为容器来存储数据的
    为什么用HashTable不用HashMap呢?
  1.  HashTable的键和值都不能为null;
  2. 上面用LinkHashMap实现的LRU,有用到 synchronized , 让线程同步去处理,这样就避免在多线程处理统一数据时造成问题
而HashTable自带同步机制的,所以多线程就能对HashTable进行正确的处理了。
    Cache的所都用有位置双连表连接起来,当一个位置被命中之后,就将通过调整链表的指向,将该位置调整到链表头的位置,新加入的Cache直接加到链表 头中。这样,在多次进行Cache操作后,
最近被命中的,就会被向链表头方向移动,而没有命中的,而想链表后面移动,链表尾则表示最近最少使用的 Cache。当需要替换内容时候,链表的最后位置就是最少被命中的位置,我们只需要淘
汰链表最后的部分即可
   
  1 public class LRUCache {
  2  
  3  private int cacheSize;
  4  private Hashtable<Object, Entry> nodes;//缓存容器
  5  private int currentSize;
  6  private Entry first;//链表头
  7  private Entry last;//链表尾
  8  
  9  public LRUCache(int i) {
 10   currentSize = 0;
 11   cacheSize = i;
 12   nodes = new Hashtable<Object, Entry>(i);//缓存容器
 13  }
 14  
 15  /**
 16   * 获取缓存中对象,并把它放在最前面
 17   */
 18  public Entry get(Object key) {
 19   Entry node = nodes.get(key);
 20   if (node != null) {
 21    moveToHead(node);
 22    return node;
 23   } else {
 24    return null;
 25   }
 26  }
 27  
 28  /**
 29   * 添加 entry到hashtable, 并把entry 
 30   */
 31  public void put(Object key, Object value) {
 32   //先查看hashtable是否存在该entry, 如果存在,则只更新其value
 33   Entry node = nodes.get(key);
 34   
 35   if (node == null) {
 36    //缓存容器是否已经超过大小.
 37    if (currentSize >= cacheSize) {
 38     nodes.remove(last.key);
 39     removeLast();
 40    } else {
 41     currentSize++;
 42    }   
 43    node = new Entry();
 44   }
 45   node.value = value;
 46   //将最新使用的节点放到链表头,表示最新使用的.
 47   node.key = key
 48   moveToHead(node);
 49   nodes.put(key, node);
 50  }
 51  
 52  /**
 53   * 将entry删除, 注意:删除操作只有在cache满了才会被执行
 54   */
 55  public void remove(Object key) {
 56   Entry node = nodes.get(key);
 57   //在链表中删除
 58   if (node != null) {
 59    if (node.prev != null) {
 60     node.prev.next = node.next;
 61    }
 62    if (node.next != null) {
 63     node.next.prev = node.prev;
 64    }
 65    if (last == node)
 66     last = node.prev;
 67    if (first == node)
 68     first = node.next;
 69   }
 70   //在hashtable中删除
 71   nodes.remove(key);
 72  }
 73  
 74  /**
 75   * 删除链表尾部节点,即使用最后 使用的entry
 76   */
 77  private void removeLast() {
 78   //链表尾不为空,则将链表尾指向null. 删除连表尾(删除最少使用的缓存对象)
 79   if (last != null) {
 80    if (last.prev != null)
 81     last.prev.next = null;
 82    else
 83     first = null;
 84    last = last.prev;
 85   }
 86  }
 87  
 88  /**
 89   * 移动到链表头,表示这个节点是最新使用过的
 90   */
 91  private void moveToHead(Entry node) {
 92   if (node == first)
 93    return;
 94   if (node.prev != null)
 95    node.prev.next = node.next;
 96   if (node.next != null)
 97    node.next.prev = node.prev;
 98   if (last == node)
 99    last = node.prev;
100   if (first != null) {
101    node.next = first;
102    first.prev = node;
103   }
104   first = node;
105   node.prev = null;
106   if (last == null)
107    last = first;
108  }
109  /*
110   * 清空缓存
111   */
112  public void clear() {
113   first = null;
114   last = null;
115   currentSize = 0;
116  }
117  
118 }
119  
120 class Entry {
121  Entry prev;//前一节点
122  Entry next;//后一节点
123  Object value;//
124  Object key;//
125 }

 参考地址:http://blog.csdn.net/beiyeqingteng/article/details/7010411

 
posted @ 2015-05-14 17:44  perfect亮  阅读(481)  评论(0)    收藏  举报