LRU cache实现 -Java 转

引子:

      我们平时总会有一个电话本记录所有朋友的电话,但是,如果有朋友经常联系,那些朋友的电话号码不用翻电话本我们也能记住,但是,如果长时间没有联系了,要再次联系那位朋友的时候,我们又不得不求助电话本,但是,通过电话本查找还是很费时间的。但是,我们大脑能够记住的东西是一定的,我们只能记住自己最熟悉的,而长时间不熟悉的自然就忘记了。

      其实,计算机也用到了同样的一个概念,我们用缓存来存放以前读取的数据,而不是直接丢掉,这样,再次读取的时候,可以直接在缓存里面取,而不用再重新查找一遍,这样系统的反应能力会有很大提高。但是,当我们读取的个数特别大的时候,我们不可能把所有已经读取的数据都放在缓存里,毕竟内存大小是一定的,我们一般把最近常读取的放在缓存里(相当于我们把最近联系的朋友的姓名和电话放在大脑里一样)。现在,我们就来研究这样一种缓存机制。

LRU Cache:

     LRU缓存利用了这样的一种思想。LRU是Least Recently Used 的缩写,翻译过来就是“最不常使用”,也就是说,LRU缓存把最不常使用的数据移除,让给最新读取的数据。大多数情况下,最常读取的,也是读取次数最多的,所以,利用LRU缓存,我们能够提高系统的performance. LRU cache是非常频繁出现的一道面试题,一般来讲,当我们问到这道题时,面试官往往想得到的答案是利用 doubly linked list + hashtable 实现 LRU Cache, 那么我们现在就来讲一讲如何利用doubly linked list + hashtable实现LRU Cache的。

对于LRU cache,往往会有以下要求:

1. 假设Cache里面的 entry 都是按照序列保存的,那么,对于新的entry,我们把它放置在最前面。

2. 如果一个entry已经存在,我们再次访问到该entry的时候,我们需要把它放在cache的最前面。

3. 当cache满了的时候,需要把最后一个entry 从cache里面移除出去,然后再往里插入 entry。

4. 以上所有的操作复杂度必须为 O(1).

      对于操作复杂度,一旦看到要求为O(1), 一般我们都会立刻想到 hashtable, 所以,为了实现“顺序”的要求,我们需要有一个链表来连接所有的entry. 所以,在实现时,我们将Cache的所有 entry 都用doubly linked list 连接起来,当一个 entry 被命中之后,就将通过调整链表的指向,将该位置调整到链表头的位置,新加入的Cache直接加到链表头中。这样,在多次进行Cache操作后,最近被命中的,就会被向链表头方向移动,而没有命中的,而想链表后面移动,链表尾则表示最近最少使用的Cache。当需要替换内容时候,链表的最后位置就是最少被命中的位置,我们只需要淘汰链表最后的部分即可。

我们首先定义entry, 每一个entry包括键(key)和 值 (value),而且,每一个 entry 都带有两个指针分别指向它们的前一个和后一个 entry.

1 package Cache;
2 
3 public class Entry
4 {
5     Entry prev;
6     Entry next;
7     Object value;
8     Object key;
9 }

再定义一个统一的接口:

 1 package Cache;
 2 
 3 public interface MyCache
 4 {
 5     public void addElement(Object key, Object value);
 6     public Object getElement(Object key);
 7     public boolean isExist(Object key);
 8     public int size();
 9     public int capacity();
10     public void clear();
11 }

在Hashtable里,我们需要保存该Entry, 这个时候,我们用Entry的键作为Hashtable 里的键,而Hashtable的值呢就是Entry。

  1 package Cache;
  2 
  3 import java.util.*;
  4 
  5 public class MyLRUCache implements MyCache
  6 {
  7     private int cacheSize;
  8     private Hashtable<Object, Entry> nodes;
  9     private int currentSize;
 10     private Entry first;
 11     private Entry last;
 12 
 13     public MyLRUCache(int i)
 14     {
 15         currentSize = 0;
 16         cacheSize = i;
 17         nodes = new Hashtable<Object, Entry>(i);
 18     }
 19 
 20     @Override
 21     public synchronized void addElement(Object key, Object value)
 22     {
 23         Entry node = nodes.get(key);
 24         if (node == null)
 25         {
 26             if (currentSize >= cacheSize)
 27             {
 28                 nodes.remove(last.key);
 29                 removeLast();
 30             } else
 31                 currentSize++;
 32             node = new Entry();
 33         }
 34         node.value = value;
 35         moveToHead(node);
 36         nodes.put(key, node);
 37     }
 38 
 39     private synchronized void moveToHead(Entry node)
 40     {
 41         if (node == first)
 42             return;
 43         if (node.prev != null)
 44             node.prev.next = node.next;
 45         if (node.next != null)
 46             node.next.prev = node.prev;
 47         if (last == node)
 48             last = node.prev;
 49         if (first != null)
 50         {
 51             node.next = first;
 52             first.prev = node;
 53         }
 54         first = node;
 55         node.prev = null;
 56         if (last == null)
 57             last = first;
 58 
 59     }
 60 
 61     private synchronized void removeLast()
 62     {
 63         if (last != null)
 64         {
 65             if (last.prev != null)
 66                 last.prev.next = null;
 67             else
 68                 first = null;
 69             last = last.prev;
 70         }
 71     }
 72 
 73     @Override
 74     public synchronized Entry getElement(Object key)
 75     {
 76         Entry node = nodes.get(key);
 77         if (node != null)
 78         {
 79             moveToHead(node);
 80             return node;
 81         } else
 82             return null;
 83     }
 84 
 85     @Override
 86     public boolean isExist(Object key)
 87     {
 88         Entry node = nodes.get(key);
 89         if (node != null)
 90             return true;
 91         return false;
 92     }
 93 
 94     @Override
 95     public int size()
 96     {
 97         return currentSize;
 98     }
 99 
100     @Override
101     public int capacity()
102     {
103         return cacheSize;
104     }
105 
106     @Override
107     public void clear()
108     {
109         first = null;
110         last = null;
111         currentSize = 0;
112 
113     }
114 
115 }

 

 

posted @ 2013-04-23 20:26  曾先森在努力  阅读(606)  评论(0编辑  收藏  举报