19aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Juddpackage com.bumptech.glide.util;
29aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
39aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Juddimport java.util.LinkedHashMap;
49aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Juddimport java.util.Map;
59aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
69aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd/**
79aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd * A general purpose size limited cache that evicts items using an LRU algorithm. By default every item is assumed to
89aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd * have a size of one. Subclasses can override {@link #getSize(Object)}} to change the size on a per item basis.
99aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd *
109aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd * @param <T> The type of the keys.
119aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd * @param <Y> The type of the values.
129aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd */
139aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Juddpublic class LruCache<T, Y> {
149aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    private final LinkedHashMap<T, Y> cache = new LinkedHashMap<T, Y>(100, 0.75f, true);
15d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd    private int maxSize;
16d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd    private final int initialMaxSize;
179aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    private int currentSize = 0;
189aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
199aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    public LruCache(int size) {
20d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd        this.initialMaxSize = size;
219aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        this.maxSize = size;
229aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
239aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
24d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd    public void setSizeMultiplier(float multiplier) {
25d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd        if (multiplier < 0) {
26d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd            throw new IllegalArgumentException("Multiplier must be >= 0");
27d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd        }
28d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd        maxSize = Math.round(initialMaxSize * multiplier);
29d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd        evict();
30d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd    }
31d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd
329aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    protected int getSize(Y item) {
339aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        return 1;
349aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
359aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
3678bad2aa32f824f9e098b5058dfa3506a7ed3f62Sam Judd    protected void onItemRemoved(T key, Y item) {  }
379aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
389aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    public int getCurrentSize() {
399aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        return currentSize;
409aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
419aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
429aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    public boolean contains(T key) {
439aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        return cache.containsKey(key);
449aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
459aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
469aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    public Y get(T key) {
479aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        return cache.get(key);
489aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
499aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
509aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    public Y put(T key, Y item) {
510e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd        final int itemSize = getSize(item);
520e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd        if (itemSize >= maxSize) {
5378bad2aa32f824f9e098b5058dfa3506a7ed3f62Sam Judd            onItemRemoved(key, item);
540e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd            return null;
550e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd        }
560e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd
579aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        final Y result = cache.put(key, item);
588ff32510d6572ca4952a87ccb8ad7140c1619443Sam Judd        if (result != item) {
598ff32510d6572ca4952a87ccb8ad7140c1619443Sam Judd            currentSize += getSize(item);
608ff32510d6572ca4952a87ccb8ad7140c1619443Sam Judd            evict();
618ff32510d6572ca4952a87ccb8ad7140c1619443Sam Judd        }
629aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        return result;
639aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
649aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
65e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd    public Y remove(T key) {
66e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd        final Y value = cache.remove(key);
67e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd        if (value != null) {
68e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd            currentSize -= getSize(value);
69e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd        }
70e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd        return value;
71e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd    }
72e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd
739aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    public void clearMemory() {
749aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        trimToSize(0);
759aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
769aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
779aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    protected void trimToSize(int size) {
789aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        Map.Entry<T, Y> last;
799aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        while (currentSize > size) {
809aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd            last = cache.entrySet().iterator().next();
819aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd            final Y toRemove = last.getValue();
829aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd            currentSize -= getSize(toRemove);
8378bad2aa32f824f9e098b5058dfa3506a7ed3f62Sam Judd            final T key = last.getKey();
8478bad2aa32f824f9e098b5058dfa3506a7ed3f62Sam Judd            cache.remove(key);
8578bad2aa32f824f9e098b5058dfa3506a7ed3f62Sam Judd            onItemRemoved(key, toRemove);
869aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        }
879aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
889aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd
899aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    private void evict() {
909aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd        trimToSize(maxSize);
919aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd    }
929aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd}
93