概述
前面我们有介绍 HashMap 的使用,HashMap 也是我们经常会使用的集合,本文我们再介绍一下 LinkedHashMap 的使用和原理。
使用方法
LinkedHashMap 一个特点就是有序的存储键值对,下面就通过一个例子来验证一下。
1 | Map<String, String> linkedHashMap = new LinkedHashMap<>(); |
1 | Test: key = key1, value = value1 |
可以看到,输出是有序的键值对,而且,是默认是按照插入顺序排序的。
其实,还有一种排序规则是按照访问顺序排序的,也就是,每次访问LinkedHashMap都会改变它的数据顺序,把当前访问的数据排到尾部。这个规则是 LruCache 的实现基础。
把上面的代码的 LinkedHashMap 构造方式改为如下:
1 | Map<String, String> linkedHashMap = new LinkedHashMap<>(0, 0.75f, true); |
1 | Test: key = key1, value = value1 |
通过打印结果可以看到,对 key2 的一次访问操作会把该记录移到尾部。
代码分析
构造方法
1 | public class LinkedHashMap<K,V> |
LinkedHashMap 继承了 HashMap 并实现了 Map 接口,所以 LinkedHashMap 可以完全代替 HashMap 的功能。
下面来看一下它的几个构造方法:
1 | public LinkedHashMap(int initialCapacity, float loadFactor) { |
这里的 accessOrder 就是上面提到的决定了 LinkedHashMap 的存储顺序。accessOrder 为 false 时是按照插入顺序排序的,为 true 时是按照 访问顺序排序的。
另外,LinkedHashMap 里面还有个 header 的变量:
1 | private transient LinkedHashMapEntry<K,V> header; |
它返回的是 LinkedHashMap 里的双向链表的头节点,LinkedHashMap 正是通过这个链表还实现有序的存储。
LinkedHashMap
1 | private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V> { |
LinkedHashMapEntry 继承 HashMapEntry ,添加了 before 和 after 属性来构建一个双向的链表。
方法介绍
init 方法
init 方法在 HashMap 的构造方法里面调用,在 LinkedHashMap 里面重写了这个方法。
1 | @Override |
这里初始化了一个只有头结点的双向链表。
get 方法
LinkedHashMap 重写了 HashMap 的 get 方法:
1 | public V get(Object key) { |
遍历数据
介绍遍历数据就要介绍一下 LinkedHashIterator:
1 | private abstract class LinkedHashIterator<T> implements Iterator<T> { |
可以看到这里其实就是遍历双向链表。
LinkedHashMap 的应用
在 Android 中提供了一个 LruCache 的缓存框架,是用 LinkedHashMap 来实现的,这个后面再介绍。
总结
- LinkedHashMap 是继承于 HashMap,是基于 HashMap 和双向链表来实现的。
- HashMap 的数据是无序的,LinkedHashMap 是有序的。
- LinkedHashMap 相对于 LinkedHashMap 存储数据的方式并没有变,只是多了一个双向链表来维持顺序。
- LinkedHashMap也是线程不安全的。