LurCache算法

LruCache的Lru指的是LeastRecentlyUsed,也就是近期最少使用算法。
使用LruCache其實(shí)非常簡(jiǎn)單,下面以一個(gè)圖片緩存為例:

創(chuàng)建LruCache對(duì)象:

private static class StringBitmapLruCache extends LruCache<String, Bitmap> {
        public StringBitmapLruCache() {
            // 構(gòu)造方法傳入當(dāng)前應(yīng)用可用最大內(nèi)存的八分之一
            super((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));
        }

        @Override
        // 重寫(xiě)sizeOf方法,并計(jì)算返回每個(gè)Bitmap對(duì)象占用的內(nèi)存
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount() / 1024;
        }

        @Override
        // 當(dāng)緩存被移除時(shí)調(diào)用,第一個(gè)參數(shù)是表明緩存移除的原因,true表示被LruCache移除,false表示被主動(dòng)remove移除,可不重寫(xiě)
        protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap
                newValue) {
            super.entryRemoved(evicted, key, oldValue, newValue);
        }

        @Override
        // 當(dāng)get方法獲取不到緩存的時(shí)候調(diào)用,如果需要?jiǎng)?chuàng)建自定義默認(rèn)緩存,可以在這里添加邏輯,可不重寫(xiě)
        protected Bitmap create(String key) {
            return super.create(key);
        }
    }

LruCache<String, Bitmap> mLruCache = new StringBitmapLruCache();

把圖片寫(xiě)入緩存:

mLruCache.put(name, bitmap);

從緩存讀取圖片:

mLruCache.get(name);

從緩存中刪除圖片:

mLruCache.remove(name);

源碼分析:

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

可以看到,構(gòu)造方法中我們獲取了緩存的最大值,并且創(chuàng)建了一個(gè)LinkedHashMap對(duì)象,這個(gè)對(duì)象就是整個(gè)LruCache的關(guān)鍵,淘汰最少使用的算法,其實(shí)就是通過(guò)這個(gè)類來(lái)實(shí)現(xiàn)的(HashMap和雙向鏈表合二為一即是LinkedHashMap。所謂LinkedHashMap,其落腳點(diǎn)在HashMap,因此更準(zhǔn)確地說(shuō),它是一個(gè)將所有Entry節(jié)點(diǎn)鏈入一個(gè)雙向鏈表的HashMap。由于LinkedHashMap是HashMap的子類,所以LinkedHashMap自然會(huì)擁有HashMap的所有特性。比如,LinkedHashMap的元素存取過(guò)程基本與HashMap基本類似,只是在細(xì)節(jié)實(shí)現(xiàn)上稍有不同。當(dāng)然,這是由LinkedHashMap本身的特性所決定的,因?yàn)樗~外維護(hù)了一個(gè)雙向鏈表用于保持迭代順序。此外,LinkedHashMap可以很好的支持LRU算法)。

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

put方法中,先計(jì)算插入的對(duì)象類型的大小,調(diào)用的方法是safeSizeOf,這個(gè)方法其實(shí)只是簡(jiǎn)單的調(diào)用了我們?cè)跇?gòu)造的時(shí)候重寫(xiě)的sizeOf方法,如果返回負(fù)數(shù),則拋出異常。接著把我們需要緩存的對(duì)象插入LinkedHashMap中,如果緩存中有這個(gè)對(duì)象,就把size復(fù)位。如果緩存中有這個(gè)key對(duì)應(yīng)的對(duì)象,則調(diào)用entryRemoved方法,這個(gè)方法默認(rèn)為空,但是如果我們需要在緩存更新之后進(jìn)行一些記錄的話,可以通過(guò)在構(gòu)造時(shí)重寫(xiě)這個(gè)方法來(lái)做到。接下來(lái),調(diào)用trimToSize方法,這個(gè)方法是去檢查當(dāng)前的size有沒(méi)有超過(guò)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 || map.isEmpty()) {
                    break;
                }

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

可以看到,這里的判斷邏輯也很簡(jiǎn)單,通過(guò)不斷的檢查,如果超過(guò)maxSize,則從LinkedHashMap中剔除一個(gè),直到size等于或者小于maxSize,這里同樣會(huì)調(diào)用entryRemoved方法。

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

這里可以看到,當(dāng)我們調(diào)用get方法的時(shí)候,直接從LinkedHashMap中g(shù)et一個(gè)當(dāng)前key的對(duì)象并返回,如果返回的為Null,則會(huì)調(diào)用create方法來(lái)創(chuàng)建一個(gè)對(duì)象,而create方法默認(rèn)也是一個(gè)空方法,直接返回null,所以,如果你需要在get失敗的時(shí)候創(chuàng)建一個(gè)默認(rèn)的對(duì)象,可以在構(gòu)造的時(shí)候重寫(xiě)create方法。如果重寫(xiě)了create方法,那么下面的代碼會(huì)被執(zhí)行,先進(jìn)行LinkedHashMap的插入方法,如果已經(jīng)存在,則返回存在的對(duì)象,否則返回我們創(chuàng)建的對(duì)象。這里可以看到,這里重復(fù)判斷列表中是否已經(jīng)存在相同的對(duì)象,原因是,如果create方法處理的時(shí)間過(guò)長(zhǎng),有可能create出來(lái)的對(duì)象已經(jīng)被put到LinkedHashMap中了

remove 方法

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

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

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