1package com.bumptech.glide.util;
2
3import java.util.LinkedHashMap;
4import java.util.Map;
5
6/**
7 * A general purpose size limited cache that evicts items using an LRU algorithm. By default every item is assumed to
8 * have a size of one. Subclasses can override {@link #getSize(Object)}} to change the size on a per item basis.
9 *
10 * @param <T> The type of the keys.
11 * @param <Y> The type of the values.
12 */
13public class LruCache<T, Y> {
14    private final LinkedHashMap<T, Y> cache = new LinkedHashMap<T, Y>(100, 0.75f, true);
15    private int maxSize;
16    private final int initialMaxSize;
17    private int currentSize = 0;
18
19    public LruCache(int size) {
20        this.initialMaxSize = size;
21        this.maxSize = size;
22    }
23
24    public void setSizeMultiplier(float multiplier) {
25        if (multiplier < 0) {
26            throw new IllegalArgumentException("Multiplier must be >= 0");
27        }
28        maxSize = Math.round(initialMaxSize * multiplier);
29        evict();
30    }
31
32    protected int getSize(Y item) {
33        return 1;
34    }
35
36    protected void onItemRemoved(T key, Y item) {  }
37
38    public int getCurrentSize() {
39        return currentSize;
40    }
41
42    public boolean contains(T key) {
43        return cache.containsKey(key);
44    }
45
46    public Y get(T key) {
47        return cache.get(key);
48    }
49
50    public Y put(T key, Y item) {
51        final int itemSize = getSize(item);
52        if (itemSize >= maxSize) {
53            onItemRemoved(key, item);
54            return null;
55        }
56
57        final Y result = cache.put(key, item);
58        if (result != item) {
59            currentSize += getSize(item);
60            evict();
61        }
62        return result;
63    }
64
65    public Y remove(T key) {
66        final Y value = cache.remove(key);
67        if (value != null) {
68            currentSize -= getSize(value);
69        }
70        return value;
71    }
72
73    public void clearMemory() {
74        trimToSize(0);
75    }
76
77    protected void trimToSize(int size) {
78        Map.Entry<T, Y> last;
79        while (currentSize > size) {
80            last = cache.entrySet().iterator().next();
81            final Y toRemove = last.getValue();
82            currentSize -= getSize(toRemove);
83            final T key = last.getKey();
84            cache.remove(key);
85            onItemRemoved(key, toRemove);
86        }
87    }
88
89    private void evict() {
90        trimToSize(maxSize);
91    }
92}
93