1 /**
2  * Copyright (c) 2011, Google Inc.
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 
17 package com.android.mail.utils;
18 
19 import java.util.LinkedHashMap;
20 import java.util.Map;
21 
22 /**
23  * A simple in-memory LRU cache, which is trivial to implement on top
24  * of JDK's {@link LinkedHashMap}.
25  *
26  * LRU policy is ensured by the underlying LinkedHashMap functionality.
27  */
28 public final class LruCache<K, V> extends LinkedHashMap<K, V> {
29     private static final long serialVersionUID = 1L;
30     private final int maxCapacity;
31 
32     /**
33      * Creates an instance of LRUCache, with given capacity.
34      *
35      * @param capacity maximum number of elements in the cache. This is also
36      * used as initialCapacity i.e. memory is allocated upfront.
37      */
LruCache(int capacity)38     public LruCache(int capacity) {
39         this(capacity, capacity);
40     }
41 
42     /**
43      * Creates an instance of LRUCache, with given capacity.
44      *
45      * @param initialCapacity initial capacity of the cache.
46      * @param maxCapacity maximum number of elements in the cache.
47      */
LruCache(int initialCapacity, int maxCapacity)48     private LruCache(int initialCapacity, int maxCapacity) {
49         super(initialCapacity, (float) 0.75, true);
50         this.maxCapacity = maxCapacity;
51     }
52 
53     // These methods are needed because the default implementation of LinkedHashMap is *not*
54     // synchronized.
55     /**
56      * Gets an element from the cache, returning the element if found, or null if not
57      * @param key
58      * @return
59      */
getElement(K key)60     public synchronized V getElement(K key) {
61         return get(key);
62     }
63 
64     /**
65      * Puts an element into the cache.
66      * @param key
67      * @param value, a non-null value.
68      */
putElement(K key, V value)69     public synchronized void putElement(K key, V value) {
70         put(key, value);
71     }
72 
73     /**
74      * Removes an element from the cache. Returns the removed element, or null if no such element
75      * existed in the cache.
76      * @param key
77      * @return
78      */
removeElement(K key)79     public synchronized V removeElement(K key) {
80         return remove(key);
81     }
82 
83     /**
84      * {@inheritDoc}
85      * <p>
86      * Necessary to override because HashMap increases the capacity of the
87      * hashtable before inserting the elements. However, here we call put() which
88      * checks if we can remove the eldest element before increasing the capacity.
89      */
90     @Override
putAll(Map<? extends K, ? extends V> m)91     public synchronized void putAll(Map<? extends K, ? extends V> m) {
92         for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
93             put(e.getKey(), e.getValue());
94         }
95     }
96 
97     /**
98      * This method is called by LinkedHashMap to check whether the eldest entry
99      * should be removed.
100      *
101      * @param eldest
102      * @return true if element should be removed.
103      */
104     @Override
removeEldestEntry(Map.Entry<K, V> eldest)105     protected synchronized boolean removeEldestEntry(Map.Entry<K, V> eldest) {
106         return size() > maxCapacity;
107     }
108 }
109