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 195f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 205f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Constructor for LruCache. 215f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 225f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param size The maximum size of the cache, the units must match the units used in {@link #getSize(Object)}. 235f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 249aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd public LruCache(int size) { 25d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd this.initialMaxSize = size; 269aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd this.maxSize = size; 279aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 289aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 295f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 305f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Sets a size multiplier that will be applied to the size provided in the constructor to set the new size of the 315f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * cache. If the new size is less than the current size, entries will be evicted until the current size is less 325f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * than or equal to the new size. 335f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 345f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param multiplier The multiplier to apply. 355f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 36d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd public void setSizeMultiplier(float multiplier) { 37d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd if (multiplier < 0) { 38d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd throw new IllegalArgumentException("Multiplier must be >= 0"); 39d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd } 40d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd maxSize = Math.round(initialMaxSize * multiplier); 41d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd evict(); 42d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd } 43d8900c3aa1844ac66a1019eefd368c83459c2c4cSam Judd 445f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 455f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Returns the size of a given item, defaulting to one. The units must match those used in the size passed in to the 465f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * constructor. Subclasses can override this method to return sizes in various units, usually bytes. 475f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 485f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param item The item to get the size of. 495f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 509aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd protected int getSize(Y item) { 519aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd return 1; 529aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 539aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 545f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 555f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * A callback called whenever an item is evicted from the cache. Subclasses can override. 565f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 575f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param key The key of the evicted item. 585f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param item The evicted item. 595f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 60de24d6a2112bebef8d42def8c1f21c79ab7d418fRobert Papp protected void onItemEvicted(T key, Y item) { 61de24d6a2112bebef8d42def8c1f21c79ab7d418fRobert Papp // optional override 62de24d6a2112bebef8d42def8c1f21c79ab7d418fRobert Papp } 639aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 645f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 655207906814e7f5983ce16db79affba28e5651d2eSam Judd * Returns the current maximum size of the cache in bytes. 665207906814e7f5983ce16db79affba28e5651d2eSam Judd */ 675207906814e7f5983ce16db79affba28e5651d2eSam Judd public int getMaxSize() { 685207906814e7f5983ce16db79affba28e5651d2eSam Judd return maxSize; 695207906814e7f5983ce16db79affba28e5651d2eSam Judd } 705207906814e7f5983ce16db79affba28e5651d2eSam Judd 715207906814e7f5983ce16db79affba28e5651d2eSam Judd /** 725f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Returns the sum of the sizes of all items in the cache. 735f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 749aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd public int getCurrentSize() { 759aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd return currentSize; 769aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 779aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 785f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 795f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Returns true if there is a value for the given key in the cache. 805f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 815f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param key The key to check. 825f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 835f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd 849aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd public boolean contains(T key) { 859aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd return cache.containsKey(key); 869aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 879aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 885f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 895f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Returns the item in the cache for the given key or null if no such item exists. 905f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 915f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param key The key to check. 925f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 939aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd public Y get(T key) { 949aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd return cache.get(key); 959aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 969aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 975f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 985f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Adds the given item to the cache with the given key and returns any previous entry for the given key that may 995f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * have already been in the cache. 1005f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 1015f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * <p> 1025f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * If the size of the item is larger than the total cache size, the item will not be added to the cache and 1035f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * instead {@link #onItemEvicted(Object, Object)} will be called synchronously with the given key and item. 1045f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * </p> 1055f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 1065f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param key The key to add the item at. 1075f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param item The item to add. 1085f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 1099aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd public Y put(T key, Y item) { 1100e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd final int itemSize = getSize(item); 1110e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd if (itemSize >= maxSize) { 1125f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd onItemEvicted(key, item); 1130e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd return null; 1140e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd } 1150e2e2b1b8df449b6e3223b090f5a55f1993e6c1fSam Judd 1169aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd final Y result = cache.put(key, item); 1175ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd if (item != null) { 1188ff32510d6572ca4952a87ccb8ad7140c1619443Sam Judd currentSize += getSize(item); 1198ff32510d6572ca4952a87ccb8ad7140c1619443Sam Judd } 1205ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd if (result != null) { 1215ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd // TODO: should we call onItemEvicted here? 1225ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd currentSize -= getSize(result); 1235ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd } 1245ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd evict(); 1255ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd 1269aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd return result; 1279aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 1289aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 1295f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 1305f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Removes the item at the given key and returns the removed item if present, and null otherwise. 1315f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 1325f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param key The key to remove the item at. 1335f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 134e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd public Y remove(T key) { 135e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd final Y value = cache.remove(key); 136e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd if (value != null) { 137e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd currentSize -= getSize(value); 138e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd } 139e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd return value; 140e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd } 141e743a1f03f24e33270f38de883b508d4312a7f69Sam Judd 1425f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 1435f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Clears all items in the cache. 1445f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 1459aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd public void clearMemory() { 1469aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd trimToSize(0); 1479aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 1489aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 1495f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd /** 1505f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * Removes the least recently used items from the cache until the current size is less than the given size. 1515f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * 1525f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd * @param size The size the cache should be less than. 1535f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd */ 1549aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd protected void trimToSize(int size) { 1559aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd Map.Entry<T, Y> last; 1569aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd while (currentSize > size) { 1579aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd last = cache.entrySet().iterator().next(); 1589aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd final Y toRemove = last.getValue(); 1599aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd currentSize -= getSize(toRemove); 16078bad2aa32f824f9e098b5058dfa3506a7ed3f62Sam Judd final T key = last.getKey(); 16178bad2aa32f824f9e098b5058dfa3506a7ed3f62Sam Judd cache.remove(key); 1625f4610b54d517be58105bcf73ce3291ba79f9f40Sam Judd onItemEvicted(key, toRemove); 1639aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 1649aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 1659aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd 1669aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd private void evict() { 1679aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd trimToSize(maxSize); 1689aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd } 1699aa6dd1e9e9497e54d38a8f5f686dd510b224ee1Sam Judd} 170