15
15
* @param <Y> The type of the values.
16
16
*/
17
17
public class LruCache <T , Y > {
18
- private final Map <T , Y > cache = new LinkedHashMap <>(100 , 0.75f , true );
18
+ private final Map <T , Entry < Y > > cache = new LinkedHashMap <>(100 , 0.75f , true );
19
19
private final long initialMaxSize ;
20
20
private long maxSize ;
21
21
private long currentSize ;
@@ -98,7 +98,8 @@ public synchronized boolean contains(@NonNull T key) {
98
98
*/
99
99
@ Nullable
100
100
public synchronized Y get (@ NonNull T key ) {
101
- return cache .get (key );
101
+ Entry <Y > entry = cache .get (key );
102
+ return entry != null ? entry .value : null ;
102
103
}
103
104
104
105
/**
@@ -109,6 +110,19 @@ public synchronized Y get(@NonNull T key) {
109
110
* the cache and instead {@link #onItemEvicted(Object, Object)} will be called synchronously with
110
111
* the given key and item.
111
112
*
113
+ * <p>The size of the item is determined by the {@link #getSize(Object)} method. To avoid errors
114
+ * where {@link #getSize(Object)} returns different values for the same object when called at
115
+ * different times, the size value is acquired in {@code put} and retained until the item is
116
+ * evicted, replaced or removed.
117
+ *
118
+ * <p>If {@code item} is null the behavior here is a little odd. For the most part it's similar to
119
+ * simply calling {@link #remove(Object)} with the given key. The difference is that calling this
120
+ * method with a null {@code item} will result in an entry remaining in the cache with a null
121
+ * value and 0 size. The only real consequence is that at some point {@link #onItemEvicted(Object,
122
+ * Object)} may be called with the given {@code key} and a null value. Ideally we'd make calling
123
+ * this method with a null {@code item} identical to {@link #remove(Object)} but we're preserving
124
+ * this odd behavior to match older versions :(.
125
+ *
112
126
* @param key The key to add the item at.
113
127
* @param item The item to add.
114
128
*/
@@ -123,17 +137,17 @@ public synchronized Y put(@NonNull T key, @Nullable Y item) {
123
137
if (item != null ) {
124
138
currentSize += itemSize ;
125
139
}
126
- @ Nullable final Y old = cache .put (key , item );
140
+ @ Nullable Entry < Y > old = cache .put (key , item == null ? null : new Entry <>( item , itemSize ) );
127
141
if (old != null ) {
128
- currentSize -= getSize ( old ) ;
142
+ currentSize -= old . size ;
129
143
130
- if (!old .equals (item )) {
131
- onItemEvicted (key , old );
144
+ if (!old .value . equals (item )) {
145
+ onItemEvicted (key , old . value );
132
146
}
133
147
}
134
148
evict ();
135
149
136
- return old ;
150
+ return old != null ? old . value : null ;
137
151
}
138
152
139
153
/**
@@ -143,11 +157,12 @@ public synchronized Y put(@NonNull T key, @Nullable Y item) {
143
157
*/
144
158
@ Nullable
145
159
public synchronized Y remove (@ NonNull T key ) {
146
- final Y value = cache .remove (key );
147
- if (value ! = null ) {
148
- currentSize -= getSize ( value ) ;
160
+ Entry < Y > entry = cache .remove (key );
161
+ if (entry = = null ) {
162
+ return null ;
149
163
}
150
- return value ;
164
+ currentSize -= entry .size ;
165
+ return entry .value ;
151
166
}
152
167
153
168
/** Clears all items in the cache. */
@@ -162,20 +177,32 @@ public void clearMemory() {
162
177
* @param size The size the cache should be less than.
163
178
*/
164
179
protected synchronized void trimToSize (long size ) {
165
- Map .Entry <T , Y > last ;
166
- Iterator <Map .Entry <T , Y >> cacheIterator ;
180
+ Map .Entry <T , Entry < Y > > last ;
181
+ Iterator <Map .Entry <T , Entry < Y > >> cacheIterator ;
167
182
while (currentSize > size ) {
168
183
cacheIterator = cache .entrySet ().iterator ();
169
184
last = cacheIterator .next ();
170
- final Y toRemove = last .getValue ();
171
- currentSize -= getSize ( toRemove ) ;
185
+ final Entry < Y > toRemove = last .getValue ();
186
+ currentSize -= toRemove . size ;
172
187
final T key = last .getKey ();
173
188
cacheIterator .remove ();
174
- onItemEvicted (key , toRemove );
189
+ onItemEvicted (key , toRemove . value );
175
190
}
176
191
}
177
192
178
193
private void evict () {
179
194
trimToSize (maxSize );
180
195
}
196
+
197
+ @ Synthetic
198
+ static final class Entry <Y > {
199
+ final Y value ;
200
+ final int size ;
201
+
202
+ @ Synthetic
203
+ Entry (Y value , int size ) {
204
+ this .value = value ;
205
+ this .size = size ;
206
+ }
207
+ }
181
208
}
0 commit comments