什么是LRU:
Least Recently Used(最近最少使用),其思想如名称,定义一个固定size的缓存池,如果缓存内容没有超过缓存池,则所有的数据都是可以直接放入到池中的,但是如果需要缓存的对象,已经超过了缓存池的限制,则LRU会将最近最少被使用到的缓存池中的对象,驱逐出池,用于腾出空位置将新对象放入其中。
什么是LinkedHashMap:
内部基于双向链表,实现了Map接口的数据结构,在使用LinkedHashMap时,可以定义内部元素存储顺序,默认是按照insert的顺序,但是也可以按照访问顺序,LRUCache的实现,就是使用了LinkedHashMap的按照访问顺序特点。—
存储顺序按照插入顺序保存,最近读取的数据放在队列的尾部,最早读取的数据放在了队列的头部,如果当前存储的数据大小已经超过定义的最大值,则新数据插入,会将队头部的数据清除。
Android实现的版本,是采用delegation的方式,如果使用inherition的方式,可以在外部直接使用Collections.sychorinzed(Map)的方式很方便的实现线程安全属性。而使用delegation的方式,则需要自己实现线程同步的问题(Android的版本是线程安全的)。
关于一个缓存工具的使用,其中必然最重要的就是get和put(add)方法的使用了,那么接下来就来分析一下其实现:
- LRUCache.get(K key)
|
|
LRUCache.put(K key, V value):
12345678910111213141516171819202122232425262728/*** Caches {@code value} for {@code key}. The value is moved to the head of* the queue.** @return the previous value mapped by {@code key}.*/public final V put(K key, V value) {if (key == null || value == null) {throw new NullPointerException("key == null || value == null");}V previous;synchronized (this) {putCount++;size += safeSizeOf(key, value);previous = map.put(key, value);if (previous != null) {size -= safeSizeOf(key, previous);}}if (previous != null) {entryRemoved(false, key, previous, value);}trimToSize(maxSize);return previous;}LRUCache.remove(K key):
123456789101112131415161718192021222324/*** Removes the entry for {@code key} if it exists.** @return the previous value mapped by {@code key}.*/public final V remove(K key) {if (key == null) {throw new NullPointerException("key == null");}V previous;synchronized (this) {previous = map.remove(key);if (previous != null) {size -= safeSizeOf(key, previous);}}if (previous != null) {entryRemoved(false, key, previous, null);}return previous;}LRUCache.trimToSize:
在1,2,3方法中,都涉及到了此方法,此方法是控制缓存的关键。在初始化定义LinkedHashMap的时候,指定了maxSize,该值可以指代缓存对象的多少,也可以指代缓存对象总体占用内存的多少。而在超出maxSize的时候,就需要trimToSize方法去清理 – 最近最少使用 –的对象。/** * 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 || map.isEmpty()) { break; } Map.Entry<K, V> toEvict = map.entrySet().iterator().next();//因为每次get都会将对象放置队尾,所以迭代器开始遍历的队首就是使用最少,最优先被evict的元素。 key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } }