概述
相信很多人看到GroupedLinkedMap
这个数据结构,觉得一脸懵,因为很少甚至都没有见到过这个数据结构。其实这个数据结构是 Glide
在实现 Bitmap
缓存池时,自己定义的一个数据结构,功能类似我们常用的 HashMap
,但是又和 HashMap
不太一样,个哦能上做了一些修改。
这个数据结构虽然简单,但是它的实现思路和背后设计的原因还是很值得我们学习和研究的,所以今天我们就来看下这个结构。
本文基于
Glide 4.13.1
源码版本学习
原理
先看这个类的定义,内部有两个变量,一个是双链表的头对象 LinkedEntry
,一个保存数据用的 HashMap
,这里也说明这个数据结构大致的功能和 HashMap
肯定是很像的,如下:
class GroupedLinkedMap<K extends Poolable, V> {
// 链表头
private final LinkedEntry<K, V> head = new LinkedEntry<>();
private final Map<K, LinkedEntry<K, V>> keyToEntry = new HashMap<>();
......
}
put
方法
我们看下它的put
方法,通过这个方法来学习它是如何保存数据的。方法内容如下:
public void put(K key, V value) {
// 先从HashMap中查找对应Key的Entry是否存在
LinkedEntry<K, V> entry = keyToEntry.get(key);
if (entry == null) {
// 如果不存在,就创建一个新的LinkedEntry对象
entry = new LinkedEntry<>(key);
// 放到链表的尾部
makeTail(entry);
// 添加到HashMap中
keyToEntry.put(key, entry);
} else {
// 如果存在,就不去更新HashMap了,也就是这里不会替换相同key值的数据
key.offer();
}
// 把value添加到对应的LinkedEntry类中
entry.add(value);
}
上面的 put
方法的逻辑是:
- 并不是直接把
key-value
存放到HashMap
中,而是把key-LinkedEntry
类型保存到HashMap
中,而这里的LinkedEntry
类是一个对 key 和 value 的封装类型 - 存放数据时,如果具有相同 key,不会替换
HashMap
中的数据,而是取出已经存在的数据,然后把value
值添加到对应的LinkedEntry
类中。这个就是和HashMap
最大的区别,HashMap
每次遇到相同 key 的数据,会替换并返回旧值。
所以接下来,我们看下 LinkedEntry
类的实现
private static class LinkedEntry<K, V> {
@Synthetic final K key;
private List<V> values;
// 双链表
LinkedEntry<K, V> next;
LinkedEntry<K, V> prev;
LinkedEntry() {
this(null);
}
LinkedEntry(K key) {
next = prev = this;
this.key = key;
}
@Nullable
public V removeLast() {
final int valueSize = size();
// 如果没有就返回null,有就返回集合最后一个
return valueSize > 0 ? values.remove(valueSize - 1) : null;
}
public int size() {
return values != null ? values.size() : 0;
}
public void add(V value) {
// 相同key的位置并不是一个值,而是一个链表
if (values == null) {
values = new ArrayList<>();
}
values.add(value);
}
}
从上面可以看到 LinkedEntry
的一些特性
- 内部有 next 和 pre 变量,相当于 GroupedLinkedMap 自己维护了一个双链表,按照访问顺序维护着链表,并没有使用 LinkHashMap 来进行实现 LRU 的逻辑。
- 通过看
add
方法,可以知道相同key
的数据,都会用一个数组集合ArrayList
进行保存,不会替换
所以综上,GroupedLinkedMap
保存数据的格式如下图所示:
get
方法
我们再来看下它的get
方法,看他是如何获取数据的。
@Nullable
public V get(K key) {
LinkedEntry<K, V> entry = keyToEntry.get(key);
// 如果不存在,也会新建一条key-LinkedEntry的记录
// 并存放,只是没有value值
if (entry == null) {
entry = new LinkedEntry<>(key);
keyToEntry.put(key, entry);
} else {
key.offer();
}
// 放到双链表的头部中
makeHead(entry);
// 找到key对应的value,如果没有则返回null,否则返回数组集合的最后一个
return entry.removeLast();
}
get
的逻辑就比较简单了,主要是:
- 到
HashMap
中获取对应 key 的数据,如果没有,就先创建一个记录保存,只是这个记录LinkedEntry
只有key
,没有value
值而已 - 把找到或者生成的
LinkedEntry
放入链表的头部位置 - 返回
LinkedEntry
中数组集合数据的最后一个,如果没有则返回 null
和 LinkedHashMap
的差异
从 GroupedLinkedMap
的实现来看,和我们常用的LinkedHashMap
类很像,但是又有一些差异,具体表现为:
- 内部确实是利用了
HashMap
来实现key-value
的保存功能,但是对于相同 key 的数据处理不一样,GroupedLinkedMap
会保存相同 key 的数据,都存在一个ArrayList
的集合中,但是LinkedHashMap/HashMap
会进行替换 GroupedLinkedMap
具有 LRU 的功能,但是并不是和我们熟知的那样,利用LinkedHashMap
来实现的,而是自己内部实现了一个类似LinkedHashMap
的功能
为什么要自己实现这个数据结构
个人觉得这里之所以没有使用LinkedHashMap
来实现,是因为和它的key有关。我们来看下在Glide图片缓存中的key是什么。因为这个类主要用在Bitmap缓存池中,我们看下其中一个策略的实现,SizeConfigStrategy#put
方法:
// SizeConfigStrategy#put 方法
public void put(Bitmap bitmap) {
int size = Util.getBitmapByteSize(bitmap);
// 用内存大小和config创建一个key
Key key = keyPool.get(size, bitmap.getConfig());
groupedMap.put(key, bitmap);
......
}
// Key的equal方法
public boolean equals(Object o) {
if (o instanceof Key) {
Key other = (Key) o;
// 当size和config相等时,就是相等的
return size == other.size && Util.bothNullOrEqual(config, other.config);
}
return false;
}
从上面可以知道,Glide缓存Bitmap
,是利用Bitmap
对象的内存sizhe
和config
作为判断依据,如果两者相等,则说明Key
相等。而在我们项目的实际开发中,一般都有一套规范,存在大量尺寸相等,配置相等的图,如果我们利用LinkedHashMap
来实现,那么每种Key只能保存一个,也就是大小配置相等的Bitmap只能保存一份,这是不利于缓存的。如果命中不了缓存的Bitmap
就无法复用,就要重新创建Bitmap对象。
而且在Android 4.4以下,Bitmap
能复用的条件就是宽度
和高度
一样,inSimpleSize
为1,这种情况下如果使用LinkedHashMap
一样有上面说的问题。
所以综上面可能的原因,Glide自己实现了一个LRU的库。
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末优快云官方认证微信卡片免费领取↓↓↓
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题