一.注释

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

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

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

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

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

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

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

二:源代码分析:

1.变量

  1. private final LinkedHashMap<K, V> map;
  2.  
  3. /** Size of this cache in units. Not necessarily the number of elements. */
  4. private int size; //count
  5. private int maxSize; //容量
  6.  
  7. private int putCount;
  8. private int createCount;
  9. private int evictionCount; //delete count
  10. private int hitCount;     //命中 count
  11. private int missCount;    //未命中 count  

2.构造函数

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

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

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

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

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

3.resize

  1. /**
  2. * Sets the size of the cache.
  3. *
  4. * @param maxSize The new maximum size.
  5. */
  6. public void resize(int maxSize) {
  7. if (maxSize <= 0) {
  8. throw new IllegalArgumentException("maxSize <= 0");
  9. }
  10.  
  11. synchronized (this) {
  12. this.maxSize = maxSize;
  13. }
  14. trimToSize(maxSize);
  15. }

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

resize 就是调用trimToSize

4.trimToSize

  1. /**
  2. * Remove the eldest entries until the total of remaining entries is at or
  3. * below the requested size.
  4. *
  5. * @param maxSize the maximum size of the cache before returning. May be -1
  6. * to evict even 0-sized elements.
  7. */
  8. public void trimToSize(int maxSize) {
  9. while (true) {
  10. K key;
  11. V value;
  12. synchronized (this) {
  13. if (size < 0 || (map.isEmpty() && size != 0)) {
  14. throw new IllegalStateException(getClass().getName()
  15. + ".sizeOf() is reporting inconsistent results!");
  16. }
  17.  
  18. if (size <= maxSize) {
  19. break;
  20. }
  21.  
  22. Map.Entry<K, V> toEvict = map.eldest();
  23. if (toEvict == null) {
  24. break;
  25. }
  26.  
  27. key = toEvict.getKey();
  28. value = toEvict.getValue();
  29. map.remove(key);
  30. size -= safeSizeOf(key, value);
  31. evictionCount++;
  32. }
  33.  
  34. entryRemoved(true, key, value, null);
  35. }
  36. }

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

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

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

5.get

  1. /**
  2. * Returns the value for {@code key} if it exists in the cache or can be
  3. * created by {@code #create}. If a value was returned, it is moved to the
  4. * head of the queue. This returns null if a value is not cached and cannot
  5. * be created.
  6. */
  7. public final V get(K key) {
  8. if (key == null) {
  9. throw new NullPointerException("key == null");
  10. }
  11.  
  12. V mapValue;
  13. synchronized (this) {
  14. mapValue = map.get(key);
  15. if (mapValue != null) {
  16. hitCount++;
  17. return mapValue;
  18. }
  19. missCount++;
  20. }
  21.  
  22. /*
  23. * Attempt to create a value. This may take a long time, and the map
  24. * may be different when create() returns. If a conflicting value was
  25. * added to the map while create() was working, we leave that value in
  26. * the map and release the created value.
  27. */
  28.  
  29. V createdValue = create(key);
  30. if (createdValue == null) {
  31. return null;
  32. }
  33.  
  34. synchronized (this) {
  35. createCount++;
  36. mapValue = map.put(key, createdValue);
  37.  
  38. if (mapValue != null) {
  39. // There was a conflict so undo that last put
  40. /**
  41. * Returns the value for {@code key} if it exists in the cache or can be
  42. * created by {@code #create}. If a value was returned, it is moved to the
  43. * head of the queue. This returns null if a value is not cached and cannot
  44. * be created.
  45. */
  46. public final V get(K key) {
  47. if (key == null) {
  48. throw new NullPointerException("key == null");
  49. }
  50.  
  51. V mapValue;
  52. synchronized (this) {
  53. mapValue = map.get(key);
  54. if (mapValue != null) {
  55. hitCount++;
  56. return mapValue;
  57. }
  58. missCount++;
  59. }
  60.  
  61. /*
  62. * Attempt to create a value. This may take a long time, and the map
  63. * may be different when create() returns. If a conflicting value was
  64. * added to the map while create() was working, we leave that value in
  65. * the map and release the created value.
  66. */
  67.  
  68. V createdValue = create(key);
  69. if (createdValue == null) {
  70. return null;
  71. }
  72.  
  73. synchronized (this) {
  74. createCount++;
  75. mapValue = map.put(key, createdValue);
  76.  
  77. if (mapValue != null) {
  78. // There was a conflict so undo that last put
  79. map.put(key, mapValue);
  80. } else {
  81. size += safeSizeOf(key, createdValue);
  82. }
  83. }
  84.  
  85. if (mapValue != null) {
  86. entryRemoved(false, key, createdValue, mapValue);
  87. return mapValue;
  88. } else {
  89. trimToSize(maxSize);
  90. return createdValue;
  91. }
  92. }map.put(key, mapValue);
  93. } else {
  94. size += safeSizeOf(key, createdValue);
  95. }
  96. }
  97.  
  98. if (mapValue != null) {
  99. entryRemoved(false, key, createdValue, mapValue);
  100. return mapValue;
  101. } else {
  102. trimToSize(maxSize);
  103. return createdValue;
  104. }
  105. }

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

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

6.put

  1. /**
  2. * Caches {@code value} for {@code key}. The value is moved to the head of
  3. * the queue.
  4. *
  5. * @return the previous value mapped by {@code key}.
  6. */
  7. public final V put(K key, V value) {
  8. if (key == null || value == null) {
  9. throw new NullPointerException("key == null || value == null");
  10. }
  11.  
  12. V previous;
  13. synchronized (this) {
  14. putCount++;
  15. size += safeSizeOf(key, value);
  16. previous = map.put(key, value);
  17. if (previous != null) {
  18. size -= safeSizeOf(key, previous);
  19. }
  20. }
  21.  
  22. if (previous != null) {
  23. entryRemoved(false, key, previous, value);
  24. }
  25.  
  26. trimToSize(maxSize);
  27. return previous;
  28. }

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

  1. previous = map.put(key, value);

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

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

总结:

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

LRUCache原理分析的更多相关文章

  1. 【构建Android缓存模块】(一)吐槽与原理分析

    http://my.oschina.net/ryanhoo/blog/93285 摘要:在我翻译的Google官方系列教程中,Bitmap系列由浅入深地介绍了如何正确的解码Bitmap,异步线程操作以 ...

  2. Handler系列之原理分析

    上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式.那么本节让我们来学习一下Handler的工作原理吧!!! 我们知道Android中我们只能在ui线程(主线程)更新ui信 ...

  3. Java NIO使用及原理分析(1-4)(转)

    转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...

  4. 原子类java.util.concurrent.atomic.*原理分析

    原子类java.util.concurrent.atomic.*原理分析 在并发编程下,原子操作类的应用可以说是无处不在的.为解决线程安全的读写提供了很大的便利. 原子类保证原子的两个关键的点就是:可 ...

  5. Android中Input型输入设备驱动原理分析(一)

    转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反 ...

  6. 转载:AbstractQueuedSynchronizer的介绍和原理分析

    简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础.使用的方法是继承,子类通过 ...

  7. Camel运行原理分析

    Camel运行原理分析 以一个简单的例子说明一下camel的运行原理,例子本身很简单,目的就是将一个目录下的文件搬运到另一个文件夹,处理器只是将文件(限于文本文件)的内容打印到控制台,首先代码如下: ...

  8. NOR Flash擦写和原理分析

    NOR Flash擦写和原理分析 1. NOR FLASH 的简单介绍 NOR FLASH 是很常见的一种存储芯片,数据掉电不会丢失.NOR FLASH支持Execute On Chip,即程序可以直 ...

  9. 使用AsyncTask异步更新UI界面及原理分析

    概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...

随机推荐

  1. 解决跨站脚本注入,跨站伪造用户请求,sql注入等http安全漏洞

    跨站脚本就是在url上带上恶意的js关键字然后脚本注入了,跨站伪造用户请求就是没有经过登陆,用超链接或者直接url上敲地址进入系统,类似于sql注入这些都是安全漏洞. sql注入 1.参数化查询预处理 ...

  2. 企业级分布式存储应用与实战FastDFS实现

    FASTDFS是什么 FastDFS是由国人余庆所开发,其项目地址:https://github.com/happyfish100 FastDFS是一个轻量级的开源分布式文件系统,主要解决了大容量的文 ...

  3. 【转】对GAMIT/GLOBK的基本认识

    1.1   GAMIT/GLOBK软件可从网络上申请下载.该软件功能强大,用途广泛,一般包括精确定位,大气层可降水汽估计和空间电离层变化分析等.后两种用途只需要用到GAMIT模块,精确定位则还需要GL ...

  4. Sql Server的艺术(四) SQL多表查询

    表的基本连接 SQL的一个重要特性就是能通过JOIN关键词,从多个交叉表中查询.分析数据. 连接表的目的 在关系数据库中,数据表设计的一个重要原则就是要避免冗余性. 减少了冗余信息,节省了数据库存储空 ...

  5. js万亿级数字转汉字的封装方法

    要求如图: 实现方法: function changeBillionToCN(c) { // 对传参进行类型处理,非字符串进行转换 if(typeof(c) != "string" ...

  6. spring之集合注入

    list: <bean id="userAction" class="com.xx.action.UserAction"> <property ...

  7. HTTP协议篇(一):多工、数据流

    管道机制.多工 管道机制(Pipelining) HTTP 1.1 引入了管道机制(Pipelining),即客户端可通过同一个TCP连接同时发送多个请求.如果客户端需要请求两个资源,以前的做法是在同 ...

  8. python的组合数据类型及其内置方法说明

    python中,数据结构是通过某种方式(例如对元素进行编号),组织在一起数据结构的集合. python常用的组合数据类型有:序列类型,集合类型和映射类型 在序列类型中,又可以分为列表和元组,字符串也属 ...

  9. 浅谈JavaScript的事件(事件处理程序)

    事件就是用户或者浏览器自身执行的某种动作.诸如click.load和mouseover,都是事件的名字.而响应某个事件的函数就叫事件处理程序.事件处理程序的名字以"on"开头,比如 ...

  10. Spring mybatis源码学习指引目录

    前言: 分析了很多方面的mybatis的源码以及与spring结合的源码,但是难免出现错综的现象,为了使源码陶冶更为有序化.清晰化,特作此随笔归纳下分析过的内容.博主也为mybatis官方提供过pul ...