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)來(lái)實(shí)現(xiàn)的(HashMap和雙向鏈表合二為一即是LinkedHashMap。所謂LinkedHashMap僚稿,其落腳點(diǎn)在HashMap,因此更準(zhǔn)確地說(shuō)贫奠,它是一個(gè)將所有Entry節(jié)點(diǎn)鏈入一個(gè)雙向鏈表的HashMap。由于LinkedHashMap是HashMap的子類(lèi)唤崭,所以LinkedHashMap自然會(huì)擁有HashMap的所有特性。比如谢肾,LinkedHashMap的元素存取過(guò)程基本與HashMap基本類(lèi)似,只是在細(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ì)象類(lèi)型的大小,調(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市寸莫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌膘茎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件披坏,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡棒拂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)谜诫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人攻旦,你說(shuō)我怎么就攤上這事±挝荩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵锋谐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我截酷,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任歧蕉,我火速辦了婚禮康铭,結(jié)果婚禮上惯退,老公的妹妹穿的比我還像新娘从藤。我一直安慰自己催跪,他們只是感情好夷野,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著悯搔,像睡著了一般骑丸。 火紅的嫁衣襯著肌膚如雪妒貌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天灌曙,我揣著相機(jī)與錄音,去河邊找鬼在刺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蚣驼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颖杏,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼输玷!你這毒婦竟也來(lái)了队丝?” 一聲冷哼從身側(cè)響起靡馁,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臭墨,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侠畔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年损晤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了软棺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尤勋。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖最冰,靈堂內(nèi)的尸體忽然破棺而出瘦棋,到底是詐尸還是另有隱情暖哨,我是刑警寧澤赌朋,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布篇裁,位于F島的核電站,受9級(jí)特大地震影響茴恰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜往枣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望分冈。 院中可真熱鬧,春花似錦雕沉、人聲如沸集乔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)倔叼。三九已至汗唱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丈攒,已是汗流浹背授霸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工际插, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碘耳,地道東北人框弛。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓辛辨,卻偏偏與公主長(zhǎng)得像瑟枫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子力奋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容