文檔處理控制欄:
* [x] 選題收集:
* [ ] 初稿整理:
* [ ] 補充校對:
* [ ] 入庫存檔:
---
---
# First
先看源碼,再用具體的demo加以驗證,我們先看一下這個LruCache這個類的大致結構和方法,如下圖所示:

這又是 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布局顯示如下(代碼就不貼了,很簡單):

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);
}
~~~
圖:



我們可以先嘗試分析一下:因為我們設置的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`是顯示不出圖片的,因為被移除掉了,其它剩余兩個可以顯示,分析就到這里,看下運行結果是不是跟我們分析的一樣:

哇!真的跟我們想的一樣耶,證明我們想的是對的。這里我們思考一下就是為什么`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);
~~~
此時它們在集合里的順序是這樣的:

那比如說我們在`put` 3 元素之前,使用了1元素,就是調用了`get("1")`方法,我們知道LinkedHashMap就會改變鏈表里元素的存儲順序,代碼是這樣的:
~~~
lruCache.put("1", bitmap1);
lruCache.put("2", bitmap2);
lruCache.get("1");
lruCache.put("3", bitmap3);
~~~
~~~
那么此時對應鏈表里的順序就是:
~~~

當我們再調用顯示的時候,循環遍歷就會優先把第一個位置的`key = "2"` 的緩存對象移除掉,保證了最近使用的原則,當然了因為把這個`max_size = 2`所以在我們執行`lruCache.put("3", bitmap3);` 時,集合最終會變成這樣:

集合里只剩下 `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