理解LruCache

理解LruCache

LRU(Least Recently Used)緩存算法,即為最近最少使用算法,它的核心思想是當(dāng)緩存滿時,會優(yōu)先淘汰那些近期最少使用的緩存對象。

LruCache是一個泛型類,它內(nèi)部采用了一個LinkedHashMap以強(qiáng)引用的方式存儲外界的緩存對象,其提供get和put方法來完成緩存的獲取和添加。

下面我們具體看下LruCache.java這個類:

public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;
    
    private int size;           // 當(dāng)前大小
    private int maxSize;        // 最大容量

    private int putCount;       // put次數(shù)
    private int createCount;    // 創(chuàng)建次數(shù)
    private int evictionCount;  // 回收次數(shù)
    private int hitCount;       // 命中次數(shù)
    private int missCount;      // 未命中次數(shù)
    ......
}

可以看到LruCache主要維護(hù)一個LinkedHashMap,主要算法原理是把最近使用的對象引用存儲在 LinkedHashMap 中。
接下來看構(gòu)造函數(shù):

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

主要設(shè)置Cache最大值maxSize,初始化一個LinkedHashMap, accessOrder 設(shè)置為 true 來實(shí)現(xiàn) LRU,利用訪問順序而不是插入順序。

public void resize(int maxSize) {
    if (maxSize <= 0) {
        throw new IllegalArgumentException("maxSize <= 0");
    }

    synchronized (this) {
        this.maxSize = maxSize;
    }
    trimToSize(maxSize);
}

重新設(shè)定Cache最大值

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

    /*
     * Attempt to create a value. This may take a long time, and the map
     * may be different when create() returns. If a conflicting value was
     * added to the map while create() was working, we leave that value in
     * the map and release the created value.
     */

    V createdValue = create(key);
    if (createdValue == null) {
        return null;
    }

    synchronized (this) {
        createCount++;
        mapValue = map.put(key, createdValue);

        if (mapValue != null) {
            // There was a conflict so undo that last put
            map.put(key, mapValue);
        } else {
            size += safeSizeOf(key, createdValue);
        }
    }

    if (mapValue != null) {
        entryRemoved(false, key, createdValue, mapValue);
        return mapValue;
    } else {
        trimToSize(maxSize);
        return createdValue;
    }
}

get方法,如果Cache中存在該item,返回item;如果不存在,則創(chuàng)建item。

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++;
        size += safeSizeOf(key, value);
        previous = map.put(key, value);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

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

    trimToSize(maxSize);
    return previous;
}

往Cache中put相應(yīng)item。

對于get和put的基本原理,這里引用一篇文章中的圖片說明圖片來源

LruCache基本原理

這張圖很好地說明了最近最少的思想。

上面幾個方法中都提到了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) {
                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);
    }
}

重新計算Cache大小,將最久沒有用到的eldest刪除。

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

    V previous;
    synchronized (this) {
        previous = map.remove(key);
        if (previous != null) {
            size -= safeSizeOf(key, previous);
        }
    }

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

    return previous;
}

刪除key

protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

當(dāng) item 被回收或者刪掉時調(diào)用。該方法當(dāng) value 被回收釋放存儲空間時被 remove 調(diào)用,或者替換 item 值時 put 調(diào)用,默認(rèn)實(shí)現(xiàn)什么都沒做,使用時可以根據(jù)需要重寫。

protected V create(K key) {
    return null;
}

同樣,可以根據(jù)需要重寫。

private int safeSizeOf(K key, V value) {
    int result = sizeOf(key, value);
    if (result < 0) {
        throw new IllegalStateException("Negative size: " + key + "=" + value);
    }
    return result;
}

protected int sizeOf(K key, V value) {
    return 1;
}

返回一組key/value的entry大小,默認(rèn)為1

public final void evictAll() {
    trimToSize(-1); // -1 will evict 0-sized elements
    }

清空Cache,trimToSize(-1)。

/**
 * 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);
}

其他一些方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容