一.注释

LRUCache的原理,基本都在注释里面描述清楚了。

/**
* A cache that holds strong references to a limited number of values. Each time
* a value is accessed, it is moved to the head of a queue. When a value is
* added to a full cache, the value at the end of that queue is evicted and may
* become eligible for garbage collection.
*
* <p>If your cached values hold resources that need to be explicitly released,
* override {@link #entryRemoved}.
*
* <p>If a cache miss should be computed on demand for the corresponding keys,
* override {@link #create}. This simplifies the calling code, allowing it to
* assume a value will always be returned, even when there's a cache miss.
*
* <p>By default, the cache size is measured in the number of entries. Override
* {@link #sizeOf} to size the cache in different units. For example, this cache
* is limited to 4MiB of bitmaps:
* <pre> {@code
* int cacheSize = 4 * 1024 * 1024; // 4MiB
* LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
* protected int sizeOf(String key, Bitmap value) {
* return value.getByteCount();
* }
* }}</pre>
*
* <p>This class is thread-safe. Perform multiple cache operations atomically by
* synchronizing on the cache: <pre> {@code
* synchronized (cache) {
* if (cache.get(key) == null) {
* cache.put(key, value);
* }
* }}</pre>
*
* <p>This class does not allow null to be used as a key or value. A return
* value of null from {@link #get}, {@link #put} or {@link #remove} is
* unambiguous: the key was not in the cache.
*
* <p>This class appeared in Android 3.1 (Honeycomb MR1); it's available as part
* of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's
* Support Package</a> for earlier releases.
*/

1.每次一个元素被访问,它会被move到队列的head位置。当某个元素加入到已经full的池里面,最久未使用的一个元素会被delete。

2.如果元素需要做特殊的释放操作,请重载entryRemoved

3.如果某个key值,没有对应的初始值,可以通过create方法提供默认值。

4.创建一个LRUCache需要复写sizeof方法。

5.这个类是线程安全的。

二:源代码分析:

1.变量

private final LinkedHashMap<K, V> map;

    /** Size of this cache in units. Not necessarily the number of elements. */
private int size; //count
private int maxSize; //容量 private int putCount;
private int createCount;
private int evictionCount; //delete count
private int hitCount;     //命中 count
private int missCount;    //未命中 count  

2.构造函数

/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}

这段代码最关键的是,new了一个LinkedHashMap,这个hashmap是可以根据访问先后次序,来控制顺序的。

LinkedHashMap:第一个参数表示初始数据是0个。

0.75f 是经典的加载因子的数值。

第三个参数true表示,以访问的先后顺序来排序。也就是做到了,容量满了以后,删除最久未访问的数据。

3.resize

    /**
* Sets the size of the cache.
*
* @param maxSize The new maximum size.
*/
public void resize(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
} synchronized (this) {
this.maxSize = maxSize;
}
trimToSize(maxSize);
}

maxsize就是容量,也就是说,队列的最大长度,当full的时候,最后一个会被delete。

resize 就是调用trimToSize

4.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) {
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);
}
}

最外面的是while true 死循环。 然后开始判读size的值。1)获取最后的一个entry,拿到后,getkey & value。

2)map remove掉它。3)然后是获取这个entry占用的大小(不一定是1)。size 减去这个值。 4)evictionCount++

5)entryRemoved 之前说过,就是可以

5.get

/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
} V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
} /*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/ V createdValue = create(key);
if (createdValue == null) {
return null;
} synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue); if (mapValue != null) {
// There was a conflict so undo that last put
/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
} V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
} /*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/ V createdValue = create(key);
if (createdValue == null) {
return null;
} synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue); if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
} if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
} if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}

1)从map中获取元素,2)没有的话,就创建一个 3)由于存在多线程问题,可能已经好了一个。所以要删除最新创建的这个,或者增加size的值

4)如果create的值不需要,就释放create的值,or trimToSize

6.put

/**
* 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;
}

map 存在一个多线程 create,put的问题。所以在put的时候,可能由其他线程已经存在了oldvalue值。所以根据

previous = map.put(key, value);

这个特性,判断previous的值,来确认是否是重复put的问题。这里关键是牵涉到size的大小问题。

其他方法,注释已经写的很清楚,不难理解。

总结:

  • LruCache 封装了 LinkedHashMap,提供了 LRU 缓存的功能;
  • LruCache 通过 trimToSize 方法自动删除最近最少访问的键值对;
  • LruCache 不允许空键值;
  • LruCache 线程安全;
  • LruCache 的源码在不同版本中不一样,需要区分
  • 继承 LruCache 时,必须要复写 sizeOf 方法,用于计算每个条目的大小。

最新文章

  1. UINavigationController的创建和相关设置---学习笔记四
  2. WPF TextBox自动滚动到最户一行
  3. 不同版本的name可以重复
  4. SQL 查询CET使用领悟
  5. Beginning Android 4 Programming Book学习
  6. iOS开发——Swift篇&amp;单例的实现
  7. nodejs抓取网页内容
  8. Effective Java2读书笔记-对于所有对象都通用的方法(二)
  9. Noldbach problem
  10. C# Windows Sockets (Winsock) 接口 (转)
  11. Android 墙纸设置代码 详细说明
  12. 使用PHPMailer发送带附件并支持HTML内容的邮件
  13. 最长上升子序列 LIS(Longest Increasing Subsequence)
  14. ZendOptimizer怎么安装?Php网站打开显示乱码
  15. logback 三
  16. LeetCode算法题-Valid Palindrome(Java实现)
  17. mysql查找一个字段属于哪个表
  18. Xdebug调试环境配置
  19. 解决jQueryUi AutoComplete在某些浏览器下无法出现候选项问题
  20. 波吉亚家族第一季/全集The Borgias 1迅雷下载

热门文章

  1. Jmeter_接口自动化基础流程概述
  2. Java8内存模型—永久代(PermGen)和元空间(Metaspace)(转)
  3. android imageview按钮按下动画效果
  4. JDBC (三)
  5. java根据模板导出pdf
  6. WEB渗透测试之漏扫神器
  7. 看得懂的区块链,看不清的ICO人心
  8. linux下统计某个进程的CPU占用和内存使用
  9. Java实现单链表的快速排序和归并排序
  10. etcd集群部署