java.util.LinkedHashMap
类继承了 java.util.HashMap
类,实现了 java.util.Map
接口。
一、LinkedHashMap特点或规范
1.1 特点
java.util.LinkedHashMap
是 java.util.Map
接口的 链表 + 哈希 实现,具有可预测的迭代顺序;与 HashMap 的不同是在 HashMap 的基础上使用了双向链表的数据结构;并按照插入顺序排序。
linkedHashMap 与 HashMap 比较:
特性 |
LinkedHashMap |
HashMap |
有序性 |
有序 |
无序 |
数据结构 |
数组 - 链表 - 红黑树 |
数组 - 链表 - 红黑树 + 双向链表 |
空键 |
允许 |
允许 |
空值 |
允许 |
允许 |
1.2 LRU缓存
LinkedHashMap 提供了一个特殊的构造函数来构建 LRU 缓存(Least Recent Used最近最少使用),构造器如下:
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { ... }
其关键参数是 **accessOrder
**,其他构造器默认为 **false
**,表示按照插入顺序进行排序;如果指定 **true
**,表示按照最近访问顺序排序,LRU 缓存将会把最近最少使用的数据移除,将最近最常使用的数据 移至链表最后(迭代顺序从末尾开始)。
可以重写 removeEldestEntry(Map.Entry)
方法,以强制在添加新 Entry 时自动删除不满足条件的 Entry。
**注意**:调用 put()
/ putAll()
/ putIfAbsent()
/ get()
/ getOrDefault()
/ replace()
/ compute()
/ computeIfAbsent()
/ computeIfPresent()
/ merge()
等方法也会对 Entry 进行访问,会对迭代顺序产生影响。
关于构建LRU缓存,见文章末尾:**构建LRU缓存**
1.3 数据结构 & 性能
数据结构:
LinkedHashMap 的数据结构在 HashMap 的基础上增加了 双端链表 的实现,与 HashMap 的 哈希数组 + 链表或红黑树 不冲突,同时存在。
性能:
- 由于需要额外维护一个双端链表,因此性能可能略低于 HashMap。
- 但对于 LinkedHashMap 的集合视图的迭代所需的时间与其的大小成正比,无论其容量多大。
- 对 HashMap 的迭代所需的时间与其容量成正比。
**注意**:LinkedHashMap 类初始化时,选择过高的初始容量所导致的性能损耗 HashMap 严重,因为此类的迭代次数不受容量影响。
二、成员变量
1 2 3 4 5 6
| transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail;
final boolean accessOrder;
|
三、构造器
3.1 遵循Map接口规范的构造器
java.util.Map
接口的构造器规范,提供一个 无参构造器,一个 参数类型为 Map 的构造器。调用父类 HashMap
的构造器,初始加载因子为默认值:0.75,其余值都为默认值。
1 2 3 4 5 6 7 8 9
| public LinkedHashMap() { super(); accessOrder = false; } public LinkedHashMap(Map<? extends K, ? extends V> m) { super(); accessOrder = false; putMapEntries(m, false); }
|
3.2 继承自HashMap的构造器
支持指定 初始容量,加载因子为默认值:0.75
1 2 3 4
| public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; }
|
支持指定 初始容量 及 加载因子。
1 2 3 4
| public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; }
|
3.3 自身构造器
1 2 3 4
| public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
|
四、方法分析
LinkedHashMap 大部分方法都沿用父类 HashMap 中的方法。
afterNodeAccess(E) 方法
每次发生访问调用后,将该 Entry 移动至链表末尾(tail),代表该 Entry 最近最长使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void afterNodeAccess(Node<K,V> e) { LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
|
afterNodeInsertion(boolean) 方法
添加新 Entry 后,判断是否需要删除最不常用(head)的 Entry。
1 2 3 4 5 6 7
| void afterNodeInsertion(boolean evict) { LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } }
|
afterNodeRemoval(Node) 方法
删除 Entry 后,断开该 Entry 的头尾链接。
1 2 3 4 5 6 7 8 9 10 11 12
| void afterNodeRemoval(Node<K,V> e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.before = p.after = null; if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; }
|
linkNodeLast() 方法
将 Entry 链接至链表末尾。
1 2 3 4 5 6 7 8 9 10
| private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p; else { p.before = last; last.after = p; } }
|
transferLinks() 方法
将两个 Entry 调换位置。
1 2 3 4 5 6 7 8 9 10 11 12
| private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst) { LinkedHashMap.Entry<K,V> b = dst.before = src.before; LinkedHashMap.Entry<K,V> a = dst.after = src.after; if (b == null) head = dst; else b.after = dst; if (a == null) tail = dst; else a.before = dst; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class LRUCache<K, V> { private LinkedHashMap<K, V> map; private int cacheSize = 16;
public LRUCache() {} public LRUCache(int cacheSize) { this.cacheSize = cacheSize; map = new LRULinkedHashMap<>(cacheSize); }
public int size() { return cacheSize; } public synchronized V get(K key) { return map.get(key); } public synchronized Collection<Map.Entry<K, V>> getAll() { return new ArrayList<Map.Entry<K, V>>(map.entrySet()); } public synchronized V put(K key, V value) { return map.put(key, value); } public synchronized void clear() { map.clear(); } }
|
LRULinkedHashMap自定义Map类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> {
private int capacity; private static final int DEFAULT_INITIAL_CAPACITY = 16; private static final float DEFAULT_LOAD_FACTOR = 0.75f;
public LRULinkedHashMap() { super(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, true); this.capacity = DEFAULT_INITIAL_CAPACITY; }
public LRULinkedHashMap(int initialCapacity) { super(initialCapacity, DEFAULT_LOAD_FACTOR, true); this.capacity = initialCapacity; }
public LRULinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); this.capacity = initialCapacity; }
@Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; } }
|