LRU cache实现

来源:互联网 发布:淘宝网络客服招聘 编辑:程序博客网 时间:2024/05/01 22:31

引子:

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

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

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。当需要替换内容时候,链表的最后位置就是最少被命中的位置,我们只需要淘汰链表最后的部分即可。

所谓LRU就是最近最少被使用的意思,LRUCache的目的就是当缓存中的数据量达到一定的程度时,添加新的数据会将最近最少被使用的旧数据删除,以保障Cache中的数据都是经常需要的数据,提高命中率。下面是一个基于linkedhashmap的LRUCache的简单实现,关键是重写LinkedHashMap的removeEldestEntry方法,在LinkedHashMap中该方法默认返回false(LRUCache本身未考虑线程安全的问题)

package test1;import java.util.LinkedHashMap;import java.util.Map;/** * Created by Administrator on 15-1-21. */public class BasicLRUCache<K, V> {    private LinkedHashMap<K, V> map;    private int cacheSize;    static final int DEFAULT_CACHE_SIZE = 2;    public BasicLRUCache(){        this(DEFAULT_CACHE_SIZE);    }    public BasicLRUCache(int cacheSize){        this.cacheSize = cacheSize;        int hashTableSize = (int)Math.ceil(cacheSize/0.75f) + 1;        map = new LinkedHashMap<K, V>(hashTableSize, 0.75f, true){            private static final long serialVersionUID = 1L;            @Override            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {                System.out.println("size=" + size() + " cacheSize=" + BasicLRUCache.this.cacheSize);                return size() > BasicLRUCache.this.cacheSize;            }        };    }    public V put(K key, V value){        return map.put(key, value);    }    public V get(Object key){        return map.get(key);    }    public static void main(String[] args){        BasicLRUCache<String, String> lruCache = new BasicLRUCache<String, String>(3);        lruCache.put("1", "1");        lruCache.put("2", "2");        lruCache.put("3", "3");        lruCache.put("4", "4");        System.out.println(lruCache.get("1"));        System.out.println(lruCache.get("2"));        System.out.println(lruCache.get("3"));        System.out.println(lruCache.get("4"));    }}
"C:\Program Files\Java\jdk1.7.0_67\bin\java" -Didea.launcher.port=7533 "-Didea.launcher.bin.path=D:\Program Files\JetBrains\IntelliJ IDEA 13.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_67\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\rt.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\access-bridge.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.7.0_67\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_67;D:\IdeaProject\test\dubbo-consumer;D:\IdeaProject\bppfRefactor\bpep-portal;D:\IdeaProject\bppfRefactor\test\target\classes;D:\Program Files\JetBrains\IntelliJ IDEA 13.1.3\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain test1.BasicLRUCachesize=1 cacheSize=3size=2 cacheSize=3size=3 cacheSize=3size=4 cacheSize=3null234Process finished with exit code 0










0 0
原创粉丝点击