文档处理控制栏:
* [x] 选题收集:
* [ ] 初稿整理:
* [ ] 补充校对:
* [ ] 入库存档:
---
---
# First
先看源码,再用具体的demo加以验证,我们先看一下这个LruCache这个类的大致结构和方法,如下图所示:
![image.png](https://user-gold-cdn.xitu.io/2017/12/19/1606e1dfd1407543?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
这又是 get(K),put(K,V), remove(K) 的方法的 给人的感觉就像是一个Map的集合嘛,又有Key ,又有value 的,再看下具体的代码:
~~~
public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;
/** Size of this cache in units. Not necessarily the number of elements. */
private int size;
private int maxSize;
private int putCount;
private int createCount;
private int evictionCount;
private int hitCount;
private int missCount;
/**
* @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);
}
~~~
看到开头,我们就明白了,哦原来这个LruCache类中维护一个LinkedHashMap的一个集合,缓存我们这个对象,而且构造方法里需要我们传入一个`maxSize`的一个值,根据上面的注释我们就明白了这个就是我们LruCache缓存对象的最大数目。
# 有什么用呢?
根据惯性思维,我们可以认为,在`put`新的缓存对象的时候,根据我们设定的最大值`remove`集合里的某些缓存对象,进而添加新的缓存对象。
# Second
根据我们的分析,我们有必要去看一下这个`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;
}
~~~
代码量也不是特别多,我们看下这个,在这个`synchronized`同步代码块里,我们看到这个 `size`,是对put进来缓存对象个数的累加,然后调用集合的`map.put`方法,返回一个对象 `previous` ,就是判断这个集合中是否添加了这个缓存对象,如果不为null,就对`size`减回去。
最后又调用一个 `trimToSize(maxSize)`方法,上面都是对添加一些逻辑的处理,那么不可能无限制添加啊,肯定有移除操作,那么我们推测这个逻辑可能在这个`trimToSize(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!");
}
//只要当前size<= maxSize 就结束循环
if (size <= maxSize || map.isEmpty()) {
break;
}
// 获取这个对象,然后从map中移除掉,保证size<=maxSize
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
~~~
注释:`Remove the eldest entries until the total of remaining entries is at or below the requested size` 大概意思是说:清除时间最久的对象直到剩余缓存对象的大小小于设置的大小。没错是我们想找的。
**这里说明一下:maxSize就是我们在构造方法里传入的,自己设置的**
~~~
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);
}
~~~
这样LruCache的核心方法 `trimToSize`方法我们就说完了,接下来我将通过实例再次验证下:
# 设置场景
> 假设我们设置maxSize 为2,布局里显示3个imageView,分别代表3张我们要显示的图片,我们添加3张图片,看看会不会显示3张?
xml布局显示如下(代码就不贴了,很简单):
![image.png](https://user-gold-cdn.xitu.io/2017/12/19/1606e1dfca963ed7?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
activity代码如下:
~~~
public final int MAX_SIZE = 2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_lru);
ImageView iv1 = (ImageView) findViewById(R.id.iv1);
ImageView iv2 = (ImageView) findViewById(R.id.iv2);
ImageView iv3 = (ImageView) findViewById(R.id.iv3);
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(),R.drawable.bg);
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(),R.drawable.header_img);
Bitmap bitmap3 = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
LruCache<String,Bitmap> lruCache = new LruCache<>(MAX_SIZE);
lruCache.put("1",bitmap1);
lruCache.put("2",bitmap2);
lruCache.put("3",bitmap3);
Bitmap bitmap = lruCache.get("1");
iv1.setImageBitmap(bitmap);
Bitmap b2 = lruCache.get("2");
iv2.setImageBitmap(b2);
Bitmap b3 = lruCache.get("3");
iv3.setImageBitmap(b3);
}
~~~
图:
![bg.png](https://user-gold-cdn.xitu.io/2017/12/19/1606e1dfc83dd3d1?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
![header_img.png](https://user-gold-cdn.xitu.io/2017/12/19/1606e1dfcaf75381?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
![ic_launcher.png](https://user-gold-cdn.xitu.io/2017/12/19/1606e1dfc8d441e2?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
我们可以先尝试分析一下:因为我们设置的MaxSize 是2 ,那么在put第三个Bitmap的时候,在`trimToSize`方法中,发现这个size是3 ,maxSize 是2,会继续向下执行,不会break,结合下面代码看下
~~~
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!");
}
//第一次循环:此时 size 是3,maxSize 是 2
//第二次循环,此时 size 是 2 ,maxSize 是 2 ,满足条件,break,结束循环
if (size <= maxSize || map.isEmpty()) {
break;
}
//获取最先添加的第一个元素
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
//移除掉第一个缓存对象
map.remove(key);
// size = 2,减去移除的元素
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
~~~
这个 `safeSizeOf` 是调用`sizeOf`方法。
那么也就是说,我们在`put`第三个`bitmap`的时候,`LruCache` 会自动帮我们移除掉第一个缓存对象,因为第一个最先添加进去,时间也最长,当然后添加的`bitmap`就是新的,最近的,那么我们推断这个`iv1`是显示不出图片的,因为被移除掉了,其它剩余两个可以显示,分析就到这里,看下运行结果是不是跟我们分析的一样:
![result.png](https://user-gold-cdn.xitu.io/2017/12/19/1606e1dfc843777c?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
哇!真的跟我们想的一样耶,证明我们想的是对的。这里我们思考一下就是为什么`LruCache`使用了这个`LinkedHashMap`,为什么`LinkedHashMap`的创造方法跟我们平时创建的不太一样,源码是这样的:
~~~
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<K, V>(0, 0.75f, true)`这句代码表示,初始容量为零,`0.75`是加载因子,表示容量达到最大容量的`75%`的时候会把内存增加一半。最后这个参数至关重要。表示访问元素的排序方式,`true`表示按照访问顺序排序,`false`表示按照插入的顺序排序。这个设置为`true`的时候,如果对一个元素进行了操作`(put、get)`,就会把那个元素放到集合的最后。
确实也是这样的,我们看下`LinkedHashMap`的源码:
~~~
/**
* Constructs an empty <tt>LinkedHashMap</tt> instance with the
* specified initial capacity, load factor and ordering mode.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @param accessOrder the ordering mode - <tt>true</tt> for
* access-order, <tt>false</tt> for insertion-order
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
~~~
里面这个`assessOrder` 注释里也说的很明白:`the ordering mode - <tt>true</tt> for * access-order, <tt>false</tt> for insertion-order` -> `true` 呢就表示会排序,`false` 就代表按照插入的顺序。默认不传就是 `false` ,而且我们每次 `get(K) put(K,V)` 的时候 会根据这个变量调整元素在集合里的位置。而这么做的目的也只有一个:保留最近使用的缓存对象,举个例子说明一下:
我们向这个集合里添加了三种元素
~~~
LruCache<String, Bitmap> lruCache = new LruCache<>(MAX_SIZE);(MAX_SIZE=2)
lruCache.put("1", bitmap1);
lruCache.put("2", bitmap2);
lruCache.put("3", bitmap3);
~~~
此时它们在集合里的顺序是这样的:
![order.png](https://user-gold-cdn.xitu.io/2017/12/20/16071cd63863b74a?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
那比如说我们在`put` 3 元素之前,使用了1元素,就是调用了`get("1")`方法,我们知道LinkedHashMap就会改变链表里元素的存储顺序,代码是这样的:
~~~
lruCache.put("1", bitmap1);
lruCache.put("2", bitmap2);
lruCache.get("1");
lruCache.put("3", bitmap3);
~~~
~~~
那么此时对应链表里的顺序就是:
~~~
![image.png](https://user-gold-cdn.xitu.io/2017/12/20/16071cd6388b79d4?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
当我们再调用显示的时候,循环遍历就会优先把第一个位置的`key = "2"` 的缓存对象移除掉,保证了最近使用的原则,当然了因为把这个`max_size = 2`所以在我们执行`lruCache.put("3", bitmap3);` 时,集合最终会变成这样:
![result.png](https://user-gold-cdn.xitu.io/2017/12/20/16071cd63a829568?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)
集合里只剩下 `1 ,3`对应的缓存对象。
至此,LruCache就说完了,如果看完的你有不明白的地方可以留言,一起讨论下~
---
参考文章
* [LRU算法还一知半解?](https://juejin.im/post/5a38dca06fb9a0451f31104b)
- 0-发现
- AndroidInterview-Q-A
- Android能让你少走弯路的干货整理
- LearningNotes
- temp
- temp11
- 部分地址
- 0-待办任务
- 待补充列表
- 0-未分类
- AndroidView事件分发与滑动冲突处理
- Spannable
- 事件分发机制详解
- 1-Java
- 1-Java-01基础
- 未归档
- 你应该知道的JDK知识
- 集合框架
- 1-Java-04合集
- Java之旅0
- Java之旅
- JAVA之旅01
- JAVA之旅02
- JAVA之旅03
- JAVA之旅04
- JAVA之旅05
- JAVA之旅06
- JAVA之旅07
- JAVA之旅08
- JAVA之旅09
- java之旅1
- JAVA之旅10
- JAVA之旅11
- JAVA之旅12
- JAVA之旅13
- JAVA之旅14
- JAVA之旅15
- JAVA之旅16
- JAVA之旅17
- JAVA之旅18
- JAVA之旅19
- java之旅2
- JAVA之旅20
- JAVA之旅21
- JAVA之旅22
- JAVA之旅23
- JAVA之旅24
- JAVA之旅25
- JAVA之旅26
- JAVA之旅27
- JAVA之旅28
- JAVA之旅29
- java之旅3
- JAVA之旅30
- JAVA之旅31
- JAVA之旅32
- JAVA之旅33
- JAVA之旅34
- JAVA之旅35
- 1-Java-05辨析
- HashMapArrayMap
- Java8新特性
- Java8接口默认方法
- 图解HashMap(1)
- 图解HashMap(2)
- 2-Android
- 2-Android-1-基础
- View绘制流程
- 事件分发
- AndroidView的事件分发机制和滑动冲突解决
- 自定义View基础
- 1-安卓自定义View基础-坐标系
- 2-安卓自定义View基础-角度弧度
- 3-安卓自定义View基础-颜色
- 自定义View进阶
- 1-安卓自定义View进阶-分类和流程
- 10-安卓自定义View进阶-Matrix详解
- 11-安卓自定义View进阶-MatrixCamera
- 12-安卓自定义View进阶-事件分发机制原理
- 13-安卓自定义View进阶-事件分发机制详解
- 14-安卓自定义View进阶-MotionEvent详解
- 15-安卓自定义View进阶-特殊形状控件事件处理方案
- 16-安卓自定义View进阶-多点触控详解
- 17-安卓自定义View进阶-手势检测GestureDetector
- 2-安卓自定义View进阶-绘制基本图形
- 3-安卓自定义View进阶-画布操作
- 4-安卓自定义View进阶-图片文字
- 5-安卓自定义View进阶-Path基本操作
- 6-安卓自定义View进阶-贝塞尔曲线
- 7-安卓自定义View进阶-Path完结篇伪
- 8-安卓自定义View进阶-Path玩出花样PathMeasure
- 9-安卓自定义View进阶-Matrix原理
- 通用类介绍
- Application
- 2-Android-2-使用
- 2-Android-02控件
- ViewGroup
- ConstraintLayout
- CoordinatorLayout
- 2-Android-03三方使用
- Dagger2
- Dagger2图文完全教程
- Dagger2最清晰的使用教程
- Dagger2让你爱不释手-终结篇
- Dagger2让你爱不释手-重点概念讲解、融合篇
- dagger2让你爱不释手:基础依赖注入框架篇
- 阅读笔记
- Glide
- Google推荐的图片加载库Glide:最新版使用指南(含新特性)
- rxjava
- 这可能是最好的RxJava2.x入门教程完结版
- 这可能是最好的RxJava2.x入门教程(一)
- 这可能是最好的RxJava2.x入门教程(三)
- 这可能是最好的RxJava2.x入门教程(二)
- 这可能是最好的RxJava2.x入门教程(五)
- 这可能是最好的RxJava2.x入门教程(四)
- 2-Android-3-优化
- 优化概况
- 各种优化
- Android端秒开优化
- apk大小优化
- 内存分析
- 混淆
- 2-Android-4-工具
- adb命令
- 一键分析Android的BugReport
- 版本控制
- git
- git章节简述
- 2-Android-5-源码
- HandlerThread 源码分析
- IntentService的使用和源码分析
- 2-Android-9-辨析
- LRU算法
- 什么是Bitmap
- 常见图片压缩方式
- 3-Kotlin
- Kotlin使用笔记1-草稿
- Kotlin使用笔记2
- kotlin特性草稿
- Kotlin草稿-Delegation
- Kotlin草稿-Field
- Kotlin草稿-object
- 4-JavaScript
- 5-Python
- 6-Other
- Git
- Gradle
- Android中ProGuard配置和总结
- gradle使用笔记
- Nexus私服搭建
- 编译提速最佳实践
- 7-设计模式与架构
- 组件化
- 组件化探索(OKR)
- 1-参考列表
- 2-1-组件化概述
- 2-2-gradle配置
- 2-3-代码编写
- 2-4-常见问题
- 2-9-值得一读
- 8-数据结构与算法
- 0临时文件
- 汉诺塔
- 8-数据-1数据结构
- HashMap
- HashMap、Hashtable、HashSet 和 ConcurrentHashMap 的比较
- 迟到一年HashMap解读
- 8-数据-2算法
- 1个就够了
- Java常用排序算法(必须掌握的8大排序算法)
- 常用排序算法总结(性能+代码)
- 必须知道的八大种排序算法(java实现)
- 9-职业
- 阅读
- 书单
- 面试
- 面试-01-java
- Java面试题全集骆昊(上)
- Java面试题全集骆昊(下)
- Java面试题全集骆昊(中)
- 面试-02-android
- 40道Android面试题
- 面试-03-开源源码
- Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
- 面试-07-设计模式
- 面试-08-算法
- 面试-09-其他
- SUMMARY
- 版权说明
- temp111