LRUCache 原理

  • Returns true if there is a value for the given key in the cache.

  • @param key The key to check.

*/

public synchronized boolean contains(T key) {

return cache.containsKey(key);

}

/**

  • Returns the item in the cache for the given key or null if no such item exists.

  • @param key The key to check.

*/

@Nullable

public synchronized Y get(T key) {

return cache.get(key);

}

/**

  • Adds the given item to the cache with the given key and returns any previous entry for the

  • given key that may have already been in the cache.

  • If the size of the item is larger than the total cache size, the item will not be added to

  • the cache and instead {@link #onItemEvicted(Object, Object)} will be called synchronously with

  • the given key and item.

  • @param key The key to add the item at.

  • @param item The item to add.

*/

public synchronized Y put(T key, Y item) {

final int itemSize = getSize(item);

if (itemSize >= maxSize) {

onItemEvicted(key, item);

return null;

}

final Y result = cache.put(key, item);

if (item != null) {

currentSize += getSize(item);

}

if (result != null) {

// TODO: should we call onItemEvicted here?

currentSize -= getSize(result);

}

evict();

return result;

}

/**

  • Removes the item at the given key and returns the removed item if present, and null otherwise.

  • @param key The key to remove the item at.

*/

@Nullable

public synchronized Y remove(T key) {

final Y value = cache.remove(key);

if (value != null) {

currentSize -= getSize(value);

}

return value;

}

/**

  • Clears all items in the cache.

*/

public void clearMemory() {

trimToSize(0);

}

/**

  • Removes the least recently used items from the cache until the current size is less than the

  • given size.

  • @param size The size the cache should be less than.

*/

protected synchronized void trimToSize(int size) {

Map.Entry<T, Y> last;

while (currentSize > size) {

last = cache.entrySet().iterator().next();

final Y toRemove = last.getValue();

currentSize -= getSize(toRemove);

final T key = last.getKey();

cache.remove(key);

onItemEvicted(key, toRemove);

}

}

private void evict() {

trimToSize(maxSize);

}

}

LruCache采用的集合是LinkedHashMap,这个集合是HashMap的基础上增加了 数据链表的功能,可以看到下面这个构造函数,第一个是初始容量100, 第二个是碰撞因子0.75(即真实容量到达总容量的75%就开始扩容),第三个是链表顺序是否按访问顺序,关于这个容器的代码分析我们放在下一篇文章,在这里我们只需要知道这个集合能记录到你访问数据的次序,最近的访问的会放在链表的前面

private final LinkedHashMap<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);

initialMaxSize:初始大小,maxSize:最大,currentSize:当前 三个成员变量,创建时this.initialMaxSize 和this.maxSize 一样。 注意setSizeMultiplier函数的作用是传入一个变化乘数,改变当前的最大容量

private final int initialMaxSize;

private int maxSize;

private int currentSize = 0;

public LruCache(int size) {

this.initialMaxSize = size;

this.maxSize = size;

}

public synchronized void setSizeMultiplier(float multiplier) {

if (multiplier < 0) {

throw new IllegalArgumentException(“Multiplier must be >= 0”);

}

maxSize = Math.round(initialMaxSize * multiplier);

evict();

}

evict意思是驱逐,就是把数据清除出缓存,把容量缩减到小于等于maxSize,while循环,处理当前大小大于入参的情况,取出链表中的一个,获取其value的大小,清除,更新当前容器容量,直到符合要求

protected synchronized void trimToSize(int size) {

Map.Entry<T, Y> last;

while (currentSize > size) {

last = cache.entrySet().iterator().next();

final Y toRemove = last.getValue();

currentSize -= getSize(toRemove);

final T key = last.getKey();

cache.remove(key);

onItemEvicted(key, toRemove);

}

}

private void evict() {

trimToSize(maxSize);

}

获取item的大小,默认现在是1 。比如要实现一个Bitmap 缓存是需要返回大小的,缓存的大小是取决于所有bitmap的总大小和,而不是总个数

protected int getSize(Y item) {

return 1;

}

一个清除元素发生的回调,让LruCache 的继承者选择自己做要的事

protected void onItemEvicted(T key, Y item) {

// optional override

}

常规操作不解释

public synchronized int getMaxSize() {

return maxSize;

}

public synchronized int getCurrentSize() {

return currentSize;

}

public synchronized boolean contains(T key) {

return cache.containsKey(key);

}

@Nullable

public synchronized Y get(T key) {

return cache.get(key);

}

put操作,首先获取待加入的item 大小,如果大于缓存最大容量,就不放进去,直接调用onItemEvicte. 小于缓存最大容量,执行放入,item不为空,更新缓存当前大小。 执行放入的结果result就是说如果之前在容器内key存在,会执行替换value的操作,这时候result!=null,需要把替换出来的item的大小减去, 作者弄了个//TODO,不知道这里是否加上onItemEvicted 的回调,个人感觉应该加上,毕竟数据被清除缓存了,通知下,怎么处理交给继承者。 最后 evict(),因为单个待处理的item大小小于缓存最大容量,但是加入后,有可能超出,这里加个维护容量的代码

public synchronized Y put(T key, Y item) {

final int itemSize = getSize(item);

if (itemSize >= maxSize) {

onItemEvicted(key, item);

return null;

}

final Y result = cache.put(key, item);

if (item != null) {

currentSize += getSize(item);

}

if (result != null) {

// TODO: should we call onItemEvicted here?

currentSize -= getSize(result);

}

evict();

return result;

}

清除key出缓存,常规操作,维护当前缓存大小

public synchronized Y remove(T key) {

final Y value = cache.remove(key);

if (value != null) {

currentSize -= getSize(value);

}

return value;

}

让缓存容量小于等于0,起到clearMemory的作用

public void clearMemory() {

trimToSize(0);

最后

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

img

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-seOiS7Dc-1715580898579)]

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

  • 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!
  • 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。

当我们在抱怨环境,抱怨怀才不遇的时候,没有别的原因,一定是你做的还不够好!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

<think> 嗯,用户想了解Android LruCache的工作原理。从对话历史看,系统要求用中文回答,数学公式要用LaTeX格式,还要生成相关问题。用户这次明确询问原理,还提供了5条站内引用,内容涉及LruCache和DiskLruCache的实现机制。 用户可能正在开发需要缓存管理的Android应用,遇到内存优化问题。虽然ta只问了原理,但深层需求应该是想解决实际开发中的性能瓶颈。从引用内容看,LruCache的核心是LinkedHashMap的访问顺序排序,这点需要重点解释。 我注意到几个关键点:首先是构造函数里LinkedHashMap的第三个参数accessOrder=true,这决定了数据按访问时间排序。然后是sizeOf()方法的重写,用于自定义计算缓存项大小。最后是条目移除时的监听机制,这在引用[4]中有提及。 用户提供的引用[5]展示了LinkedHashMap的访问顺序特性,这个例子很直观——被访问过的元素会移到末尾,这正是LRU算法的体现。在解释trimToSize()时,需要用示意图说明队列头部的淘汰逻辑。 数学方面虽然用不上复杂公式,但可以用$O(1)$表示LinkedHashMap的操作复杂度。回答结构上将按初始化、存取、淘汰三个环节展开,最后补充Android特有的Bitmap缓存场景。 生成的问题要覆盖延伸场景:比如三级缓存架构的协同原理,或者ConcurrentModificationException这种实际开发可能遇到的异常。用户作为开发者应该会对这些感兴趣。 </think> 以下是Android LruCache原理详解: --- ### **一、核心原理** LruCache(Least Recently Used Cache)基于**最近最少使用算法**管理内存缓存,优先淘汰最久未访问的数据。其核心实现依赖于 `LinkedHashMap`: 1. **数据结构** 内部通过 `LinkedHashMap<K, V>` 存储数据,构造函数中设置 `accessOrder=true`: ```java this.map = new LinkedHashMap<K, V>(0, 0.75f, true); ``` 当 `accessOrder=true` 时,LinkedHashMap 按**访问顺序**排序(最新访问的数据移至队尾)。 2. **访问顺序维护** - 调用 `get(key)` 时,LinkedHashMap 会通过 `afterNodeAccess()` 方法将节点移至链表尾部。 - 调用 `put(key, value)` 时,若 key 已存在,同样会移动节点至尾部。 3. **容量控制** - 初始化时指定最大容量 `maxSize`(单位通常为条目数或内存字节数)。 - 每次添加数据后检查当前大小,若超过 `maxSize` 则触发淘汰: ```java while (currentSize > maxSize) { Map.Entry<K, V> toEvict = map.eldest(); // 获取链表头部(最久未访问) remove(toEvict.key); // 移除该条目 } ``` --- ### **二、关键源码分析** 1. **构造函数** ```java public LruCache(int maxSize) { this.maxSize = maxSize; this.map = new LinkedHashMap<>(0, 0.75f, true); // 启用访问顺序排序 } ``` 2. **`sizeOf()` 方法** 用户需重写此方法以计算每个缓存项的大小(例如Bitmap的字节数): ```java @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } ``` 3. **淘汰机制** 添加数据时调用 `trimToSize()` 确保容量合规: ```java public void trimToSize(int maxSize) { while (size > maxSize) { Map.Entry<K, V> toEvict = map.eldest(); // 获取队头条目 size -= sizeOf(toEvict.key, toEvict.value); // 更新当前大小 map.remove(toEvict.key); // 移除数据 } } ``` --- ### **三、工作流程示例** 假设缓存容量为3,依次插入A、B、C: ``` 初始: [A, B, C] (尾部C最新) 访问B后: [A, C, B] (B移至尾部) 插入D后: [C, B, D] (移除最久未访问的A) ``` - **队头**:最久未访问的数据(优先淘汰) - **队尾**:最新访问的数据 --- ### **四、在Android中的典型应用** ```java // 初始化LruCache(以Bitmap缓存为例) int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; // 分配1/8内存 LruCache<String, Bitmap> memoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount() / 1024; // 返回KB为单位的大小 } }; ``` --- ### **五、与DiskLruCache的关系** 1. **三级缓存架构**: - `LruCache`:内存缓存(高速但易失) - `DiskLruCache`:磁盘缓存(持久化存储) - 网络下载:终极数据源 2. **协同流程**: 请求数据时按 **内存 → 磁盘 → 网络** 顺序查找,获取后逐级缓存[^1][^2]。 --- ### **六、算法复杂度** - **插入/访问**:$O(1)$ (LinkedHashMap哈希表+链表) - **淘汰**:$O(1)$ (直接移除链表头部) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值