GuavaCache學習筆記一:自定義LRU算法的緩存實現


前言

今天在看GuavaCache緩存相關的源碼,這里想到先自己手動實現一個LRU算法。於是乎便想到LinkedHashMap和LinkedList+HashMap, 這里僅僅是作為簡單的復習一下。

LRU

LRU(Least recently used,最近最少使用)算法根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是“如果數據最近被訪問過,那么將來被訪問的幾率也更高”。

代碼實現原理

LinkedList + HashMap: LinkedList其實是一個雙向鏈表,我們可以通過get和put來設置最近請求key的位置,然后hashMap去存儲數據
LinkedHashMap:LinkedHashMap是繼承自HashMap,只不過Map中的Node節點改為了雙向節點,雙向節點可以維護添加的順序,在LinkedHashMap的構造函數中有一個accessOrder, 當設置為true后,put和get會自動維護最近請求的位置到last。

LinkedList+HashMap代碼實現

LRUCache接口:

/**
 * @Description:
 * @Author: wangmeng
 * @Date: 2018/12/8-10:49
 */
public class LinkedListLRUTest {
    public static void main(String[] args) {
        LRUCache<String, String> cache = new LinkedListLRUCache<>(3);
        cache.put("1", "1");
        cache.put("2", "2");
        cache.put("3", "3");
        System.out.println(cache);

        cache.put("4", "4");
        System.out.println(cache);

        System.out.println(cache.get("2"));
        System.out.println(cache);
    }
}

LinkedList實現:

/**
 * @Description:使用LinkedList+HashMap來實現LRU算法
 * @Author: wangmeng
 * @Date: 2018/12/8-10:41
 */
public class LinkedListLRUCache<K, V> implements LRUCache<K, V> {

    private final int limit;
    private final LinkedList<K> keys = new LinkedList<>();
    private final Map<K, V> cache = Maps.newHashMap();

    public LinkedListLRUCache(int limit) {
        this.limit = limit;
    }

    @Override
    public void put(K key, V value) {
        Preconditions.checkNotNull(key);
        Preconditions.checkNotNull(value);
        if (keys.size() >= limit) {
            K oldesKey = keys.removeFirst();
            cache.remove(oldesKey);
        }

        keys.addLast(key);
        cache.put(key, value);
    }

    @Override
    public V get(K key) {
        boolean exist = keys.remove(key);
        if (!exist) {
            return null;
        }

        keys.addLast(key);
        return cache.get(key);
    }

    @Override
    public void remove(K key) {

        boolean exist = keys.remove(key);
        if (exist) {
            keys.remove(key);
            cache.remove(key);
        }
    }

    @Override
    public int size() {
        return keys.size();
    }

    @Override
    public void clear() {
        keys.clear();
        cache.clear();
    }

    @Override
    public int limit() {
        return this.limit;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (K key : keys) {
            builder.append(key).append("=").append(cache.get(key)).append(";");
        }
        return builder.toString();
    }
}

LinkedList測試類:

/**
 * @Description:
 * @Author: wangmeng
 * @Date: 2018/12/8-10:49
 */
public class LinkedListLRUTest {
    public static void main(String[] args) {
        LRUCache<String, String> cache = new LinkedListLRUCache<>(3);
        cache.put("1", "1");
        cache.put("2", "2");
        cache.put("3", "3");
        System.out.println(cache);

        cache.put("4", "4");
        System.out.println(cache);

        System.out.println(cache.get("2"));
        System.out.println(cache);
    }
}

LinkedList測試類返回值:

1=1;2=2;3=3;
2=2;3=3;4=4;
2
3=3;4=4;2=2;

LinkedHashMap實現

/**
 * @Description: 不是一個線程安全的類,這里是使用LinkedHashMap來做LRU算法
 * @Author: wangmeng
 * @Date: 2018/12/8-10:14
 */
public class LinkedHashLRUCache<K, V> implements LRUCache<K, V> {

    private static class InternalLRUCache<K, V> extends LinkedHashMap<K, V> {

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

        //實現remove元素的方法,這個是重寫了LinkedHashMap中的方法。因為在HashMap的putVal會調用afterNodeInsertion(), 而這個方法會判斷removeEldestEntry方法。
        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > limit;
        }
    }


    private final int limit;
    //使用組合關系優於繼承,這里只對外暴漏LRUCache中的方法
    private final InternalLRUCache<K, V> internalLRUCache;
    public LinkedHashLRUCache(int limit) {
        Preconditions.checkArgument(limit > 0, "The limit big than zero.");
        this.limit = limit;
        this.internalLRUCache = new InternalLRUCache(limit);

    }

    @Override
    public void put(K key, V value) {
        this.internalLRUCache.put(key, value);
    }

    @Override
    public V get(K key) {
        return this.internalLRUCache.get(key);
    }

    @Override
    public void remove(K key) {
        this.internalLRUCache.remove(key);
    }

    @Override
    public int size() {
        return this.internalLRUCache.size();
    }

    @Override
    public void clear() {
        this.internalLRUCache.clear();
    }

    @Override
    public int limit() {
        return this.limit;
    }

    @Override
    public String toString() {
        return internalLRUCache.toString();
    }
}

LinkedHashMap測試類:

/**
 * @Description:
 * @Author: wangmeng
 * @Date: 2018/12/8-10:30
 */
public class LinkedHashLRUTest {
    public static void main(String[] args) {
        LRUCache<String, String> cache = new LinkedHashLRUCache<>(3);
        cache.put("1", "1");
        cache.put("2", "2");
        cache.put("3", "3");
        System.out.println(cache);

        cache.put("4", "4");
        System.out.println(cache);

        System.out.println(cache.get("2"));
        System.out.println(cache);
    }
}

LinkedHashMap測試結果:

{1=1, 2=2, 3=3}
{2=2, 3=3, 4=4}
2
{3=3, 4=4, 2=2}





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM