LRUCache原理的简单分析

119 阅读2分钟

现在大多数的内存缓存都是用这个LRUCache来进行缓存的,算法主要就是 最近最少使用 

但是其中的原理,有时候没有稍微深入研究的话,还是停留下会用的程度上(会用还不行了 :)(捂脸)

深入源码之前先简单的使用一下,图片缓存大致就是这样使用的

//获取应用程序的最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;

LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(cacheSize) {
    @Override
    protected int sizeOf(String key, Bitmap bitmap) {
        //这里一定要复写 ,返回的是bitmap的大小
        return bitmap.getByteCount();
    }
};
Bitmap bitmap = createBitmap() ;

lruCache.put("url1", bitmap);
lruCache.put("url2", bitmap);
lruCache.put("url3", bitmap);

Bitmap bitmap1 = lruCache.get("url1")

上面就是正常的使用;然后就是查看源码中为什么是会是 最近最少使用的算法

  • new 对象需要做的操作

public LruCache(int maxSize) {
    if (maxSize <= 0) {
        throw new IllegalArgumentException("maxSize <= 0");
    }
    //maxSize就是我们必须传入的大小限制,
    this.maxSize = maxSize;
    //注意这里就是使用LinkedHashMap来进行内存缓存的策略
    this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}put
  • put的操作,map的put操作

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++;
        //这里的safeSizeOf 最后会调用sizeOf方法,如果我们复写这个方法就使用,没有复写就返回 1 
        size += safeSizeOf(key, value);
        //这里调用map的put方法,如果previous存在,将这个替换的previous 减去它的大小;
        previous = map.put(key, value);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }
    //如果复写这个方法,就会调用,一般不用关心
    if (previous != null) {
        entryRemoved(false, key, previous, value);
    }
    //记住下面的方法,就是删除的操作!!!下面会重点介绍
    trimToSize(maxSize);
    return previous;
}

  • get操作 , map的get方法

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++;
            return mapValue;
        }
        missCount++;
    }
}

  • trimToSize操作,这是重点!!!

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 || map.isEmpty()) {
                break;
            }
            //如果size > maxSize 就进行删除操作
            //这里就是取出map集合的第一个,进行remove,然后再次调用SizeOf更新size。
            Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
            key = toEvict.getKey();
            value = toEvict.getValue();
            map.remove(key);
            size -= safeSizeOf(key, value);
            evictionCount++;
        }

        entryRemoved(true, key, value, null);
    }
}

其实LRUCache源码里看的差不多是对LinkedHashMap的封装!主要还是要去查询LinkedHashMap的源码才能知道为什么trimToSize中的方法里map取迭代器的第一个就是删除最近少使用的那个item了。