1. 程式人生 > >Guava---快取之LRU演算法

Guava---快取之LRU演算法

簡介

LRU全稱是Least Recently Used,即最近最久未使用的意思。

LRU演算法的設計原則是:如果一個數據在最近一段時間沒有被訪問到,那麼在將來它被訪問的可能性也很小。也就是說,當限定的空間已存滿資料時,應當把最久沒有被訪問到的資料淘汰。

實現LRU

1.用一個數組來儲存資料,給每一個數據項標記一個訪問時間戳,每次插入新資料項的時候,先把陣列中存在的資料項的時間戳自增,並將新資料項的時間戳置為0並插入到陣列中。每次訪問陣列中的資料項的時候,將被訪問的資料項的時間戳置為0。當陣列空間已滿時,將時間戳最大的資料項淘汰。

2.利用一個連結串列來實現,每次新插入資料的時候將新資料插到連結串列的頭部;每次快取命中(即資料被訪問),則將資料移到連結串列頭部;那麼當連結串列滿的時候,就將連結串列尾部的資料丟棄。

3.利用連結串列和hashmap。當需要插入新的資料項的時候,如果新資料項在連結串列中存在(一般稱為命中),則把該節點移到連結串列頭部,如果不存在,則新建一個節點,放到連結串列頭部,若快取滿了,則把連結串列最後一個節點刪除即可。在訪問資料的時候,如果資料項在連結串列中存在,則把該節點移到連結串列頭部,否則返回-1。這樣一來在連結串列尾部的節點就是最近最久未訪問的資料項。

對於第一種方法,需要不停地維護資料項的訪問時間戳,另外,在插入資料、刪除資料以及訪問資料時,時間複雜度都是O(n)。對於第二種方法,連結串列在定位資料的時候時間複雜度為O(n)。所以在一般使用第三種方式來是實現LRU演算法。

LinkedHashMap中LRU演算法實現

/**
 * @Author: Kingcym
 * @Description:  非執行緒安全
 * @Date: 2018/11/11 19:09
 */
public class LinkedHashLRUcache<k, v> {
    /**
     * LinkedHashMap(自身實現了LRU演算法)
     * 1.有序
     * 2.每次訪問一個元素,都會提到最後面去
     */
    private static class InternalLRUcache<k, v> extends LinkedHashMap<k, v> {
        private final int limit;

        private InternalLRUcache(int limit) {
            super(16, 0.75f, true);
            this.limit = limit;
        }

        //是否刪除最老的資料
        @Override
        protected boolean removeEldestEntry(Map.Entry<k, v> eldest) {
            return size() > limit;
        }
    }

    private final int limit;
    private final InternalLRUcache<k, v> internalLRUcache;


    public LinkedHashLRUcache(int limit) {
        Assert.state(limit > 0, "limit必須大於0");
        this.limit = limit;
        this.internalLRUcache = new InternalLRUcache(limit);
    }


 
    public void put(k key, v value) {
        this.internalLRUcache.put(key, value);
    }

    public v get(k key) {
        return this.internalLRUcache.get(key);
    }

    public void remove(k key) {
        this.internalLRUcache.remove(key);
    }

    public int size() {
        return this.internalLRUcache.size();
    }

    public void clear() {
        this.internalLRUcache.clear();
    }

    public String toString() {
        return internalLRUcache.toString();
    }
}

當存在熱點資料時,LRU的效率很好,但偶發性的、週期性的批量操作會導致LRU命中率急劇下降,快取汙染情況比較嚴重。