LinkedHashMap
LruCache中维护了一个LinkedHashMap实例。缓存的实现主要由LinkedHashMap来完成。
LinkedHashMap继承自HashMap,拥有HashMap的特性,比如线程不安全,初始容量是16,装载因子0.75等。
不同的是
其用双向循环链表把所有数据串在了一起。
LinkedHashMap中有两个重要的变量,分别是header和accessOrder,
如下:
/**
* The head of the doubly linked list.
*/
private transient LinkedHashMapEntry<K,V> header;
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
private final boolean accessOrder;
header代表链表的头部,accessOrder代表链表的排序方式(true:访问顺序,false:插入顺序)。其中当accessOrder为true时,每put或get一个元素,这个元素就会被放到链表尾部,也就是说,链表头部是最近最少使用的元素。根据这一点就可以来做缓存了。
/**
* This method is invoked by the superclass whenever the value
* of a pre-existing entry is read by Map.get or modified by Map.set.
* If the enclosing Map is access-ordered, it moves the entry
* to the end of the list; otherwise, it does nothing.
*/
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
上面的方法在get方法中调用,可以看到它对accessOrder进行了判断,addBefor中的内容如下
private void addBefore(LinkedHashMapEntry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
也就是将该元素移到链表末尾的操作。从而保证链表头部是最近最少使用的元素。
LruCache
LruCache中维护了一个LinkedHashMap。需要注意的是,LindedHashMap不允许null键和null值。
在初始化LinkedHashMap时,构造方法有一个入参是maxSize。在每次执行put或者get操作时,
会把当前集合的大小和maxSize进行比较,若超过最大值,则移除最近最少使用的元素。
/**
* Remove the eldest entries until the total of remaining entries is at or
* below the requested size.
*
* @param maxSize the maximum size of the cache before returning. May be -1
* to evict even 0-sized elements.
*/
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize) {
break;
}
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}