LruCache

101 阅读2分钟
public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;

    //缓存的当前容量
    private int size;
    //缓存的最大容量
    private int maxSize;

    //插入操作次数
    private int putCount;
    //未命中-创建数
    private int createCount;
    //容量满了之后,移除操作的次数
    private int evictionCount;
    //命中数
    private int hitCount;
    //没有命中数
    private int missCount;

    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }


    //调整map的容量
    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) {
        //key为空抛出异常
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            //获取对应的缓存对象
            //get()方法会实现将访问的元素更新到队列头部的功能
            mapValue = map.get(key);
            if (mapValue != null) {
                //命中数加一
                hitCount++;
                return mapValue;
            }
            //没有命中数加一
            missCount++;
        }

        //如果没有命中,是否要自定义创建
        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }

        synchronized (this) {
            //未命中-创建数
            createCount++;
            mapValue = map.put(key, createdValue);

            if (mapValue != null) {
                //创建的时候,如果另一个元素刚加入进去,则不使用创建的,使用之前添加的
                //后面不再计算大小,因为正常添加的流程已经计算过大小了,
                map.put(key, mapValue);
            } else {
                //使用创建的之后计算容量大小
                size += safeSizeOf(key, createdValue);
            }
        }
        //如果之前刚添加,则回调,自定义生成的对象,正常添加的对象
        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            //使用自定义生成的对象,需要校验map的容量
            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) {
            //插入的缓存对象值加1
            putCount++;
            //增加已有缓存的大小
            size += safeSizeOf(key, value);
            //向map中加入缓存对象
            previous = map.put(key, value);
            //如果已有缓存对象,则缓存大小恢复到之前
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }
        //entryRemoved()是个空方法,可以自行实现
        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }

        trimToSize(maxSize);
        return previous;
    }

    public void trimToSize(int maxSize) {
        ////死循环
        while (true) {
            K key;
            V value;
            synchronized (this) {
                //如果map为空并且缓存size不等于0或者缓存size小于0,抛出异常
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }
                //如果缓存大小size小于最大缓存,不需要再删除缓存对象,跳出循环
                if (size <= maxSize) {
                    break;
                }

                //迭代器获取第一个对象,即队尾的元素,近期最少访问的元素
                Map.Entry<K, V> toEvict = map.eldest();
                if (toEvict == null) {
                    break;
                }
                
                key = toEvict.getKey();
                value = toEvict.getValue();
                //删除该对象,并更新缓存大小
                map.remove(key);
                size -= safeSizeOf(key, value);
                //移除数加一
                evictionCount++;
            
            }

            entryRemoved(true, key, value, null);
            //移除之后循环操作,直到实际容量小于最大容量
        }
    }

    public final V remove(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V previous;
        synchronized (this) {
            //移除key对应的元素
            previous = map.remove(key);
            //如果该元素有值,则重新计算大小
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, null);
        }

        return previous;
    }
    
    //每次remove都会调用
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

    //数据未命中,自定义创建V
    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;
    }

    /**
     * Returns the size of the entry for {@code key} and {@code value} in
     * user-defined units.  The default implementation returns 1 so that size
     * is the number of entries and max size is the maximum number of entries.
     *
     * <p>An entry's size must not change while it is in the cache.
     */
    protected int sizeOf(K key, V value) {
        return 1;
    }

    /**
     * Clear the cache, calling {@link #entryRemoved} on each removed entry.
     */
    public final void evictAll() {
        trimToSize(-1); // -1 will evict 0-sized elements
    }

    /**
     * For caches that do not override {@link #sizeOf}, this returns the number
     * of entries in the cache. For all other caches, this returns the sum of
     * the sizes of the entries in this cache.
     */
    public synchronized final int size() {
        return size;
    }

    /**
     * For caches that do not override {@link #sizeOf}, this returns the maximum
     * number of entries in the cache. For all other caches, this returns the
     * maximum sum of the sizes of the entries in this cache.
     */
    public synchronized final int maxSize() {
        return maxSize;
    }

    /**
     * Returns the number of times {@link #get} returned a value that was
     * already present in the cache.
     */
    public synchronized final int hitCount() {
        return hitCount;
    }

    /**
     * Returns the number of times {@link #get} returned null or required a new
     * value to be created.
     */
    public synchronized final int missCount() {
        return missCount;
    }

    /**
     * Returns the number of times {@link #create(Object)} returned a value.
     */
    public synchronized final int createCount() {
        return createCount;
    }

    /**
     * Returns the number of times {@link #put} was called.
     */
    public synchronized final int putCount() {
        return putCount;
    }

    /**
     * Returns the number of values that have been evicted.
     */
    public synchronized final int evictionCount() {
        return evictionCount;
    }

    /**
     * Returns a copy of the current contents of the cache, ordered from least
     * recently accessed to most recently accessed.
     */
    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);
    }
}