zoukankan      html  css  js  c++  java
  • Java容器解析系列(17) LruCache详解

    在之前讲LinkedHashMap的时候,我们说起可以用来实现LRU(least recent used)算法,接下来我看一下其中的一个具体实现-----android sdk 中的LruCache.

    关于Lru算法,请参考漫画:什么是LRU算法?

    talk is cheap, I am gonna show you something really expensive.

    package android.util;// 该类是从Android sdk 中摘录
    
    public class LruCache<K, V> {
    
        private final LinkedHashMap<K, V> map;// 这里的 LinkedHashMap 为 android.jar 中的类,与jdk中的有所区别,这里不做区分
        private int size;// 当前 cache 的大小
        private int maxSize;// cache 最大容量
        private int putCount;// put() 调用的次数
        private int createCount;// get()未命中,成功构建新 key:value 的次数
        private int evictionCount;// 因cache大小超过容量限制,将 key:value 从 cache 中驱逐的次数
        private int hitCount;// get()命中的次数
        private int missCount;// get()未命中的次数
    
        // 构造时设置最大容量
        public LruCache(int maxSize) {
            if (maxSize <= 0) {
                throw new IllegalArgumentException("maxSize <= 0");
            }
            this.maxSize = maxSize;
            // 这里传入的 LinkedHashMap 的 accessOrder 为true,表示 其中的链表中的结点顺序为 "按访问顺序"
            this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
        }
    
        // 重新设置 cache 最大容量
        public void resize(int maxSize) {
            if (maxSize <= 0) {
                throw new IllegalArgumentException("maxSize <= 0");
            }
            synchronized (this) {
                this.maxSize = maxSize;
            }
            trimToSize(maxSize);
        }
    
        public final V get(K key) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
            V mapValue;
            synchronized (this) {// 同步访问
                mapValue = map.get(key);
                if (mapValue != null) {
                    hitCount++;// 命中次数 + 1
                    return mapValue;
                }
                missCount++;// 未命中次数 + 1
            }
            V createdValue = create(key);// 通过 key 构建一个value(比如从文件中读入value)
            if (createdValue == null) {// 创建失败
                return null;
            }
            synchronized (this) {
                createCount++;// 构建 value 成功次数 + 1
                mapValue = map.put(key, createdValue);// 添加到 LinkedHashMap 中
                if (mapValue != null) {
                    // LinkedHashMap 原来有该key的值(在构建value的时候,被其他线程添加进去的),这里重新把原来的值放进去,cache大小没有增加
                    map.put(key, mapValue);
                } else {
                    size += safeSizeOf(key, createdValue);// cache 大小增加
                }
            }
            if (mapValue != null) {
                entryRemoved(false, key, createdValue, mapValue);
                return mapValue;
            } else {
                trimToSize(maxSize);// 大小增加了,保证在最大容量范围内
                return createdValue;
            }
        }
    
        public final V put(K key, V value) {
            if (key == null || value == null) {
                throw new NullPointerException("key == null || value == null");
            }
            V previous;
            synchronized (this) {
                putCount++;// put 次数
                size += safeSizeOf(key, value);
                previous = map.put(key, value);
                if (previous != null) {
                    size -= safeSizeOf(key, previous);// 替换已有的value
                }
            }
            if (previous != null) {
                entryRemoved(false, key, previous, value);
            }
            trimToSize(maxSize);
            return previous;
        }
    
        // 移除LRU键值对,保证 cache 的大小在 maxSize 范围内
        public void trimToSize(int maxSize) {
            while (true) {
                K key;
                V value;
                synchronized (this) {
                    if (size < 0 || (map.isEmpty() && size != 0)) {
                        throw new IllegalStateException(getClass().getName()
                                + ".sizeOf() is reporting inconsistent results!");
                    }
                    if (size <= maxSize) {
                        break;
                    }
                    Map.Entry<K, V> toEvict = map.eldest();// 最老的结点,返回head结点,也即 LRU结点
                    if (toEvict == null) {// 没有可以删除的key:value了
                        break;
                    }
                    key = toEvict.getKey();
                    value = toEvict.getValue();
                    map.remove(key);
                    size -= safeSizeOf(key, value);// 大小降低
                    evictionCount++;// 驱逐次数 + 1
                }
                entryRemoved(true, key, value, null);
            }
        }
    
        public final V remove(K key) {
            if (key == null) {
                throw new NullPointerException("key == null");
            }
            V previous;
            synchronized (this) {
                previous = map.remove(key);// 通过 LinkedHashMap 移除
                if (previous != null) {
                    size -= safeSizeOf(key, previous);// cache总大小降低
                }
            }
            if (previous != null) {
                entryRemoved(false, key, previous, null);
            }
            return previous;
        }
    
        // key:oldValue 被 移除 或 驱逐 时回调
        protected void entryRemoved(boolean evicted/*是否是被驱逐(因cache大小超过容量限制被删除)*/,
                                    K key, V oldValue, V newValue) {
        }
    
        // 根据指定 key,构建 value
        protected V create(K key) {
            return null;
        }
    
        private int safeSizeOf(K key, V value) {
            int result = sizeOf(key, value);
            if (result < 0) {
                throw new IllegalStateException("Negative size: " + key + "=" + value);
            }
            return result;
        }
    
        // key:value 键值对大小,用于计算cache大小
        // 可根据情况自定义,默认为 1
        // 该 key:value 在 cache 中时大小不应该变化
        protected int sizeOf(K key, V value) {
            return 1;
        }
    
        public final void evictAll() {
            trimToSize(-1); // -1 will evict 0-sized elements
        }
    
        // 各种方法,返回成员变量,省略
        public synchronized final Map<K, V> snapshot() {
            return new LinkedHashMap<K, V>(map);
        }
    
        @Override
        public synchronized final String toString() {
            int accesses = hitCount + missCount;
            int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
            return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
                    maxSize, hitCount, missCount, hitPercent);
        }
    
    }
    
    
  • 相关阅读:
    %和format的区别
    C++ 使用 curl 进行 http 请求(GET、POST、Download)的封装
    C++ log4cplus 类库的封装
    linux top 命令
    python使用urllib2 http 下载参数的try catch
    C 小白的 thrift 环境搭建
    pandas 必背函数操作
    flask + MySQL-python 创建 webapp 应用
    python 的 virtualenv 环境搭建及 sublime 手动创建运行环境
    nginx proxy_pass指令’/’注意事项
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/10942437.html
Copyright © 2011-2022 走看看