blob: 401548800ec8f269e611ead2951700fcc0c411f9 [file] [log] [blame]
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -08001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.util;
18
19import java.util.LinkedHashMap;
20import java.util.Map;
21
22/**
23 * A cache that holds strong references to a limited number of values. Each time
24 * a value is accessed, it is moved to the head of a queue. When a value is
25 * added to a full cache, the value at the end of that queue is evicted and may
26 * become eligible for garbage collection.
27 *
28 * <p>If your cached values hold resources that need to be explicitly released,
Jesse Wilson7db1b402011-02-25 16:38:40 -080029 * override {@link #entryRemoved}.
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -080030 *
31 * <p>If a cache miss should be computed on demand for the corresponding keys,
32 * override {@link #create}. This simplifies the calling code, allowing it to
33 * assume a value will always be returned, even when there's a cache miss.
34 *
35 * <p>By default, the cache size is measured in the number of entries. Override
Jesse Wilson34895b02011-02-10 13:50:24 -080036 * {@link #sizeOf} to size the cache in different units. For example, this cache
37 * is limited to 4MiB of bitmaps:
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -080038 * <pre> {@code
Jesse Wilson34895b02011-02-10 13:50:24 -080039 * int cacheSize = 4 * 1024 * 1024; // 4MiB
40 * LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
41 * protected int sizeOf(String key, Bitmap value) {
42 * return value.getByteCount();
43 * }
44 * }}</pre>
45 *
46 * <p>This class is thread-safe. Perform multiple cache operations atomically by
47 * synchronizing on the cache: <pre> {@code
48 * synchronized (cache) {
49 * if (cache.get(key) == null) {
50 * cache.put(key, value);
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -080051 * }
Jesse Wilson34895b02011-02-10 13:50:24 -080052 * }}</pre>
Jesse Wilson56b6ad32011-02-11 13:32:04 -080053 *
54 * <p>This class does not allow null to be used as a key or value. A return
55 * value of null from {@link #get}, {@link #put} or {@link #remove} is
56 * unambiguous: the key was not in the cache.
Jesse Wilson02db1b62011-11-17 09:44:27 -050057 *
58 * <p>This class appeared in Android 3.1 (Honeycomb MR1); it's available as part
59 * of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's
60 * Support Package</a> for earlier releases.
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -080061 */
62public class LruCache<K, V> {
63 private final LinkedHashMap<K, V> map;
64
65 /** Size of this cache in units. Not necessarily the number of elements. */
66 private int size;
Jesse Wilson9b5a9352011-02-10 11:19:09 -080067 private int maxSize;
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -080068
69 private int putCount;
70 private int createCount;
71 private int evictionCount;
72 private int hitCount;
73 private int missCount;
74
75 /**
76 * @param maxSize for caches that do not override {@link #sizeOf}, this is
77 * the maximum number of entries in the cache. For all other caches,
78 * this is the maximum sum of the sizes of the entries in this cache.
79 */
80 public LruCache(int maxSize) {
81 if (maxSize <= 0) {
82 throw new IllegalArgumentException("maxSize <= 0");
83 }
84 this.maxSize = maxSize;
85 this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
86 }
87
88 /**
Jeff Browne5360fb2011-10-31 17:48:13 -070089 * Sets the size of the cache.
Jeff Browne5360fb2011-10-31 17:48:13 -070090 *
Narayan Kamatha1226a72014-02-20 10:27:55 +000091 * @param maxSize The new maximum size.
Jeff Browne5360fb2011-10-31 17:48:13 -070092 */
93 public void resize(int maxSize) {
94 if (maxSize <= 0) {
95 throw new IllegalArgumentException("maxSize <= 0");
96 }
97
98 synchronized (this) {
99 this.maxSize = maxSize;
100 }
101 trimToSize(maxSize);
102 }
103
104 /**
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800105 * Returns the value for {@code key} if it exists in the cache or can be
106 * created by {@code #create}. If a value was returned, it is moved to the
107 * head of the queue. This returns null if a value is not cached and cannot
108 * be created.
109 */
Jesse Wilson7db1b402011-02-25 16:38:40 -0800110 public final V get(K key) {
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800111 if (key == null) {
Jesse Wilson56b6ad32011-02-11 13:32:04 -0800112 throw new NullPointerException("key == null");
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800113 }
114
Jesse Wilson7db1b402011-02-25 16:38:40 -0800115 V mapValue;
116 synchronized (this) {
117 mapValue = map.get(key);
118 if (mapValue != null) {
119 hitCount++;
120 return mapValue;
121 }
122 missCount++;
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800123 }
124
Jesse Wilson7db1b402011-02-25 16:38:40 -0800125 /*
126 * Attempt to create a value. This may take a long time, and the map
127 * may be different when create() returns. If a conflicting value was
128 * added to the map while create() was working, we leave that value in
129 * the map and release the created value.
130 */
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800131
Jesse Wilson7db1b402011-02-25 16:38:40 -0800132 V createdValue = create(key);
133 if (createdValue == null) {
134 return null;
135 }
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800136
Jesse Wilson7db1b402011-02-25 16:38:40 -0800137 synchronized (this) {
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800138 createCount++;
Jesse Wilson7db1b402011-02-25 16:38:40 -0800139 mapValue = map.put(key, createdValue);
140
141 if (mapValue != null) {
142 // There was a conflict so undo that last put
143 map.put(key, mapValue);
144 } else {
145 size += safeSizeOf(key, createdValue);
146 }
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800147 }
Jesse Wilson7db1b402011-02-25 16:38:40 -0800148
149 if (mapValue != null) {
150 entryRemoved(false, key, createdValue, mapValue);
151 return mapValue;
152 } else {
153 trimToSize(maxSize);
154 return createdValue;
155 }
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800156 }
157
158 /**
159 * Caches {@code value} for {@code key}. The value is moved to the head of
160 * the queue.
161 *
Jesse Wilsonc4e62092011-02-25 17:57:04 -0800162 * @return the previous value mapped by {@code key}.
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800163 */
Jesse Wilson7db1b402011-02-25 16:38:40 -0800164 public final V put(K key, V value) {
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800165 if (key == null || value == null) {
Jesse Wilson56b6ad32011-02-11 13:32:04 -0800166 throw new NullPointerException("key == null || value == null");
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800167 }
168
Jesse Wilson7db1b402011-02-25 16:38:40 -0800169 V previous;
170 synchronized (this) {
171 putCount++;
172 size += safeSizeOf(key, value);
173 previous = map.put(key, value);
174 if (previous != null) {
175 size -= safeSizeOf(key, previous);
176 }
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800177 }
Jesse Wilson7db1b402011-02-25 16:38:40 -0800178
179 if (previous != null) {
180 entryRemoved(false, key, previous, value);
181 }
182
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800183 trimToSize(maxSize);
184 return previous;
185 }
186
Jesse Wilson7db1b402011-02-25 16:38:40 -0800187 /**
Jeff Sharkeyd96b5852012-07-27 17:00:24 -0700188 * Remove the eldest entries until the total of remaining entries is at or
189 * below the requested size.
190 *
Jesse Wilson7db1b402011-02-25 16:38:40 -0800191 * @param maxSize the maximum size of the cache before returning. May be -1
Jeff Sharkeyd96b5852012-07-27 17:00:24 -0700192 * to evict even 0-sized elements.
Jesse Wilson7db1b402011-02-25 16:38:40 -0800193 */
Jeff Sharkeyd96b5852012-07-27 17:00:24 -0700194 public void trimToSize(int maxSize) {
Jesse Wilson7db1b402011-02-25 16:38:40 -0800195 while (true) {
196 K key;
197 V value;
198 synchronized (this) {
199 if (size < 0 || (map.isEmpty() && size != 0)) {
200 throw new IllegalStateException(getClass().getName()
201 + ".sizeOf() is reporting inconsistent results!");
202 }
203
204 if (size <= maxSize) {
205 break;
206 }
207
208 Map.Entry<K, V> toEvict = map.eldest();
209 if (toEvict == null) {
210 break;
211 }
212
213 key = toEvict.getKey();
214 value = toEvict.getValue();
215 map.remove(key);
216 size -= safeSizeOf(key, value);
217 evictionCount++;
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800218 }
219
Jesse Wilsonc4e62092011-02-25 17:57:04 -0800220 entryRemoved(true, key, value, null);
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800221 }
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800222 }
223
224 /**
Jesse Wilson56b6ad32011-02-11 13:32:04 -0800225 * Removes the entry for {@code key} if it exists.
226 *
Jesse Wilsonc4e62092011-02-25 17:57:04 -0800227 * @return the previous value mapped by {@code key}.
Jesse Wilson56b6ad32011-02-11 13:32:04 -0800228 */
Jesse Wilson7db1b402011-02-25 16:38:40 -0800229 public final V remove(K key) {
Jesse Wilson56b6ad32011-02-11 13:32:04 -0800230 if (key == null) {
231 throw new NullPointerException("key == null");
232 }
233
Jesse Wilson7db1b402011-02-25 16:38:40 -0800234 V previous;
235 synchronized (this) {
236 previous = map.remove(key);
237 if (previous != null) {
238 size -= safeSizeOf(key, previous);
239 }
Jesse Wilson56b6ad32011-02-11 13:32:04 -0800240 }
Jesse Wilson7db1b402011-02-25 16:38:40 -0800241
242 if (previous != null) {
243 entryRemoved(false, key, previous, null);
244 }
245
Jesse Wilson56b6ad32011-02-11 13:32:04 -0800246 return previous;
247 }
248
249 /**
Jesse Wilson7db1b402011-02-25 16:38:40 -0800250 * Called for entries that have been evicted or removed. This method is
251 * invoked when a value is evicted to make space, removed by a call to
252 * {@link #remove}, or replaced by a call to {@link #put}. The default
253 * implementation does nothing.
254 *
255 * <p>The method is called without synchronization: other threads may
256 * access the cache while this method is executing.
257 *
258 * @param evicted true if the entry is being removed to make space, false
259 * if the removal was caused by a {@link #put} or {@link #remove}.
260 * @param newValue the new value for {@code key}, if it exists. If non-null,
261 * this removal was caused by a {@link #put}. Otherwise it was caused by
262 * an eviction or a {@link #remove}.
263 */
264 protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800265
266 /**
267 * Called after a cache miss to compute a value for the corresponding key.
268 * Returns the computed value or null if no value can be computed. The
269 * default implementation returns null.
Jesse Wilson7db1b402011-02-25 16:38:40 -0800270 *
271 * <p>The method is called without synchronization: other threads may
272 * access the cache while this method is executing.
273 *
274 * <p>If a value for {@code key} exists in the cache when this method
275 * returns, the created value will be released with {@link #entryRemoved}
276 * and discarded. This can occur when multiple threads request the same key
277 * at the same time (causing multiple values to be created), or when one
278 * thread calls {@link #put} while another is creating a value for the same
279 * key.
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800280 */
281 protected V create(K key) {
282 return null;
283 }
284
285 private int safeSizeOf(K key, V value) {
286 int result = sizeOf(key, value);
287 if (result < 0) {
288 throw new IllegalStateException("Negative size: " + key + "=" + value);
289 }
290 return result;
291 }
292
293 /**
294 * Returns the size of the entry for {@code key} and {@code value} in
295 * user-defined units. The default implementation returns 1 so that size
296 * is the number of entries and max size is the maximum number of entries.
297 *
298 * <p>An entry's size must not change while it is in the cache.
299 */
300 protected int sizeOf(K key, V value) {
301 return 1;
302 }
303
304 /**
Jesse Wilsonc4e62092011-02-25 17:57:04 -0800305 * Clear the cache, calling {@link #entryRemoved} on each removed entry.
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800306 */
Jesse Wilson7db1b402011-02-25 16:38:40 -0800307 public final void evictAll() {
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800308 trimToSize(-1); // -1 will evict 0-sized elements
309 }
310
311 /**
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800312 * For caches that do not override {@link #sizeOf}, this returns the number
313 * of entries in the cache. For all other caches, this returns the sum of
314 * the sizes of the entries in this cache.
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800315 */
316 public synchronized final int size() {
317 return size;
318 }
319
320 /**
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800321 * For caches that do not override {@link #sizeOf}, this returns the maximum
322 * number of entries in the cache. For all other caches, this returns the
323 * maximum sum of the sizes of the entries in this cache.
324 */
325 public synchronized final int maxSize() {
326 return maxSize;
327 }
328
329 /**
Jesse Wilsona460c182011-03-16 14:57:01 -0700330 * Returns the number of times {@link #get} returned a value that was
331 * already present in the cache.
Jesse Wilsone2c1f4a2011-02-07 14:26:26 -0800332 */
333 public synchronized final int hitCount() {
334 return hitCount;
335 }
336
337 /**
338 * Returns the number of times {@link #get} returned null or required a new
339 * value to be created.
340 */
341 public synchronized final int missCount() {
342 return missCount;
343 }
344
345 /**
346 * Returns the number of times {@link #create(Object)} returned a value.
347 */
348 public synchronized final int createCount() {
349 return createCount;
350 }
351
352 /**
353 * Returns the number of times {@link #put} was called.
354 */
355 public synchronized final int putCount() {
356 return putCount;
357 }
358
359 /**
360 * Returns the number of values that have been evicted.
361 */
362 public synchronized final int evictionCount() {
363 return evictionCount;
364 }
365
366 /**
367 * Returns a copy of the current contents of the cache, ordered from least
368 * recently accessed to most recently accessed.
369 */
370 public synchronized final Map<K, V> snapshot() {
371 return new LinkedHashMap<K, V>(map);
372 }
373
374 @Override public synchronized final String toString() {
375 int accesses = hitCount + missCount;
376 int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
377 return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
378 maxSize, hitCount, missCount, hitPercent);
379 }
380}