• 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;
        }
    
    }
    

      

      

  • 相关阅读:
    SharePoint的安装配置
    onkeypress与onkeydown及 oncopy和onpaste 事件区别详细说明
    sql Server 的基本函数
    iOS 错误 之 http请求
    iOS 错误及解决汇总
    iOS 开源库 之 AFNetWorking 2.x
    linux 下 tomcat 之 配置静态资源路径
    iOS 之 Block Variable
    iOS 协议
    #import与@class的区别
  • 原文地址:https://www.cnblogs.com/chuiyuan/p/4836877.html
Copyright © 2020-2023  润新知