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()+"
");
        }
        cache.put("5", "five"); //5 2 4
        cache.put("4", "second four");//4 5 2
        //verify cache content
        sb.append("cache used:"+cache.usedEntries()+"
") ;
        sb.append(cache.get("4")+"
");
        sb.append(cache.get("5")+"
");
        sb.append(cache.get("2")+"
");
        for (Map.Entry<String,String > e : cache.getAll()){
            sb.append(e.getKey()+":"+e.getValue()+"
");
        }
        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;
    }

}

  

  

原文地址:https://www.cnblogs.com/chuiyuan/p/4836877.html