Android之LRU算法

LRU算法簡介

LRU 全稱是 least recently used,意爲“最近最少使用”,說白了就是一種淘汰算法,當有新的元素插入進來的時候,我們的使用空間又有限的時候,就需要淘汰舊的元素,這時候就會選擇淘汰最近最少使用的元素。

 

應用場景

在app開發中,假設有一個頁面列表需要從網絡獲取大量圖片,在頁面切換、滑動中,同一張圖片不可能每次都去網絡獲取,這樣對內存、性能和用戶體驗都是一個考驗,最好的做法是設置本地文件緩存和內存緩存,存儲從網絡取得的數據。
但本地文件緩存空間並非是無限大,容量越大讀取效率越低,可設置一個折中緩存容量比如10M,如果緩存已滿,我們需要採用合適的替換策略換掉一個已有的數據對象,並替之已一個新的數據對象;內存緩存作爲最先被讀取的數據,應該存儲那些經常使用的數據對象,且內存容量有限,內存緩存的容量也應該限定。

 

實現原理

Android中有一個LRU算法實現的集合LruCache。

public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;
}

可以看出LruCache內部是使用LinkedHashMap存儲數據的。其實LruCache主要依靠LinkedHashMap本身的LRU實現來實現的。LruCache只是進行了另一次封裝。

    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
//     這裏指定了該集合的最大容量,一旦集合容量大於該容量則會調用trimToSize方法來減少容量。
        this.maxSize = maxSize;
//      這裏創建了LinkedHashMap並且第三個參數指定爲true.該參數爲true時LinkedHashMap開啓LRU算法。
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

在使用LruCache時要重寫sizeOf方法,來指定成員的實際容量。否則默認返回1

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

LruCache的一個回調函數本身沒有實現,可重新。在新增或移除變量時會被調用。第一個參數爲true時是移除變量,false時是新增變量。

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

接下來簡單說下LruCache的存放方法。

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;
    }
/**
*簡單來說就是會無限循環的來檢測內存容量
*如果內存容量大於maxSize,則會移除最近最久使用的成員。
*然後繼續循環,直到內存容量小於maxSize或者集合爲null循環終止
**/
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);
        }
    }
public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }
//     如果值存在則直接返回值,這裏會依賴LinkedHashMap的LRU機制。
        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }

//      這裏會調用create方法,這個方法本身默認返回null,即如果用戶不重寫get方法會返回null。
//      根據實際需求來編寫。如果創建了新的對象則會存放進集合中。
        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;
        }
    }

 

資料源自互聯網,經綜合整理而成,如有侵犯,請聯繫我。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章