LRU原理与实现
一、LRU是什么
Least Recently Used,也就是最近最少使用,LRU缓存将最近最少使用的数据移除,让给最新读取的数据。而往往是最常读取的,也就是读取次数最多的。所以利用LRU缓存,我们可能提高系统的performance.
二、LRU的实现
- 实现方法一:使用 LinkedHashMap
使用LinkedHashMap的好处:
代码如下
public class MyLRUCache1<K,V>{ private static final float hashTableLoadFactor = 0.75f; private LinkedHashMap<K,V > map ; private int cacheSize ; public MyLRUCache1( int cacheSize){ this.cacheSize = cacheSize ; int hashTableCapacity = (int)Math.ceil(cacheSize/hashTableLoadFactor)+1; //an anonymous inner class,true means accessOrder map = new LinkedHashMap<K,V>(hashTableCapacity, hashTableLoadFactor, true) { private static final long serialVersionID=1; @Override protected boolean removeEldestEntry(Entry<K, V> eldest) { //在什么情况下进行回收 return size()> MyLRUCache1.this.cacheSize ; } }; } public synchronized V get(K key){ return map.get(key) ; } public synchronized void put(K key,V value){ map.put(key, value) ; } public synchronized void clear (){ map.clear(); } public synchronized int usedEntries(){ return map.size() ; } public synchronized Collection<Map.Entry<K,V>> getAll(){ return new ArrayList<Map.Entry<K,V>>(map.entrySet()) ; } }
测试方法
StringBuilder sb = new StringBuilder() ; cache.put("1", "one"); //1 cache.put("2", "two"); //2 1 cache.put("3", "three"); //3 2 1 cache.put("4", "four"); //4 3 2 if (cache.get("2")== null) throw new Error() ; //2 4 3 for (Map.Entry<String,String > e : cache.getAll()){ sb.append(e.getKey()+":"+e.getValue()+"\n"); } cache.put("5", "five"); //5 2 4 cache.put("4", "second four");//4 5 2 //verify cache content sb.append("cache used:"+cache.usedEntries()+"\n") ; sb.append(cache.get("4")+"\n"); sb.append(cache.get("5")+"\n"); sb.append(cache.get("2")+"\n"); for (Map.Entry<String,String > e : cache.getAll()){ sb.append(e.getKey()+":"+e.getValue()+"\n"); } tv.setText(sb.toString()); Log.d(TAG,sb.toString()) ;
结果如下
3:three
4:four
2:two
cache used:3
second four
five
two
4:second four
5:five
2:two
- 实现方法2:双链表+HashTable
源码如下
public class MyLRUCache2<K,V> { private class Entry { Entry prev ; Entry next ; V value ; K key ; } private int cacheSize ; private Hashtable<K, Entry> nodes ; //cache container private int currentSize ; private Entry first ; //header private Entry last ; //last public MyLRUCache2(int cacheSize ){ currentSize =0; this.cacheSize = cacheSize ; nodes = new Hashtable<>(cacheSize); } /** *得到cache中的对象,并放到最前面 */ public Entry get(K key ){ Entry node = nodes.get(key) ; if (node!= null){ moveToHead(node); return node; }else { return null ; } } /** *添加entry到hashtable */ public void put(K key, V value){ //先查看hashtable是否存在这个entry,如果存在,则只更新其value Entry node = nodes.get(key) ; if (node == null){ //缓存容器是否已经超过大小 if (currentSize>= cacheSize){ nodes.remove(last.key) ; removeLast(); }else { currentSize++; } node= new Entry(); } node.value = value; //将最新使用的节点放到链表头最新使用的 moveToHead(node) ; nodes.put(key,node); } /** * 将entry删除,删除只在cache满了才会被执行 */ public void remove(K key){ Entry node = nodes.get(key) ; //在链表中删除 if (node!= null){ if (node.prev!= null){ node.prev.next = node.next ; } if (node.next!= null){ node.next.prev = node.prev ; } if (last== node){ last = node.prev ; } if (first == node){ first = node.next ; } } //在hashtable中删除 nodes.remove(key) ; } /** * 删除链表尾部节点,也就是最少使用的缓存对象 */ private void removeLast(){ // 链表尾部不为空,则将其指向空,删除链表尾部, if (last!= null){ if (last.prev!= null){ last.prev.next = null ; }else { first = null ; } last= last.prev ; } } /** * 移动到链表头,表示这个节点是最新使用过的 */ private void moveToHead( Entry node){ if (node == first) return; if (node.prev!= null){ node.prev.next = node.next; } if (node.next!= null){ node.next.prev = node.prev; } if (last== node){ last = node.prev ; } if (first!= null) { node.next = first ; first.prev = node; } first = node; node.prev = null ; if (last == null){ last = first ; } } public void clear (){ first = null ; last = null ; currentSize =0; } }