LinkedHashMap 源码分析
2022/6/10 1:22:30
本文主要是介绍LinkedHashMap 源码分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
分析
LinkedHashMap
是HashMap
的子类,也就是说它与HashMap
具有相同存储结构,不同的是,LinkedHashMap
加入了一个双向循环链表,链表的头结点是一个不保存数据的head
节点。
/** * The head of the doubly linked list. */ private transient Entry<K,V> header;
HashMap
中Entry
有一个指向下一个Entry
的next
属性,形成了一个单向链表。而LinkedHashMap
中继承了HashMap
的Entry
,多了before
和after
:
/** * LinkedHashMap entry. */ private static class Entry<K,V> extends HashMap.Entry<K,V> { // These fields comprise the doubly linked list used for iteration. Entry<K,V> before, after; }
链表的形成过程如下图所示:
- 初始的时候,head 的 before 和 after 都指向自己
- put 时,将新元素添加到链表末尾,这时候各自的 before 和 after 该如何指向,看图说话
- remove 时同理
知道这个双向循环链表的形成过程,再看源码就简单许多
package java.util; import java.io.*; public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> { private static final long serialVersionUID = 3801124242820219131L; /** * 双线链表的头结点(空节点) */ private transient Entry<K,V> header; /** * 是否根据访问顺序排序 * 默认false,根据插入顺序排序 * true 时,根据访问(put修改或get)顺序排序 */ private final boolean accessOrder; /** * 构造:容量 加载因子 * accessOrder = false; */ public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; } /** * 构造:容量 */ public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } public LinkedHashMap() { super(); accessOrder = false; } public LinkedHashMap(Map<? extends K, ? extends V> m) { super(m); accessOrder = false; } /** * 构造方法:可指定accessOrder */ public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; } /** * 初始化 head 节点,这个方法在 HashMap 是空实现 * head 节点不存储任何内容 * head 的 after 和 before 都指向自己 */ @Override void init() { header = new Entry<>(-1, null, null, null); header.before = header.after = header; } /** * 重写 transfer 方法 * transfer 作用是 rehash,在 HashMap 中是循环数组然后链表 * LinkedHashMap 中是直接循环双向链表,性能更加 */ @Override void transfer(HashMap.Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e = header.after; e != header; e = e.after) { if (rehash) e.hash = (e.key == null) ? 0 : hash(e.key); int index = indexFor(e.hash, newCapacity); e.next = newTable[index]; newTable[index] = e; } } /** * 是否包含value,循环双向列表 */ public boolean containsValue(Object value) { // Overridden to take advantage of faster iterator if (value==null) { for (Entry e = header.after; e != header; e = e.after) if (e.value==null) return true; } else { for (Entry e = header.after; e != header; e = e.after) if (value.equals(e.value)) return true; } return false; } /** * 与 HashMap 中不同的是,如果accessOrder,也就是按访问顺序,那么将当前节点放到链表的末尾 */ public V get(Object key) { Entry<K,V> e = (Entry<K,V>)getEntry(key); if (e == null) return null; e.recordAccess(this); return e.value; } /** * 清楚还是和 HashMap 一样,只不过要对双向链表做处理,就是回到了初始状态 * header 节点的 before 和 after 都指向自己 */ public void clear() { super.clear(); header.before = header.after = header; } /** * LinkedHashMap entry. * 继承自 HashMap.Entry<K,V> * 多了 before after 属性,双向循环列表的关键 */ private static class Entry<K,V> extends HashMap.Entry<K,V> { // These fields comprise the doubly linked list used for iteration. Entry<K,V> before, after; Entry(int hash, K key, V value, HashMap.Entry<K,V> next) { super(hash, key, value, next); } /** * 新增方法,用于删除双向列表节点 * 看上图自己体会,再看代码 */ private void remove() { before.after = after; after.before = before; } /** * 在指定节点前插入节点,同样看图 */ private void addBefore(Entry<K,V> existingEntry) { after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; } /** * 这个方法在 HashMap.Entry<K,V> 中是空实现,这里重写 * 如果是访问顺序,那么移除双向列表中当前节点,添加到列表末尾,也就是 header 节点前面 * 如果是默认,也就是插入顺序,啥也不干 */ void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } } void recordRemoval(HashMap<K,V> m) { remove(); } } // LinkedHashMap 抽象迭代器,没有实现 next() 方法 private abstract class LinkedHashIterator<T> implements Iterator<T> { // nextEntry 初始为 header 下一个节点 Entry<K,V> nextEntry = header.after; Entry<K,V> lastReturned = null; // 快速失败标志 int expectedModCount = modCount; // 迭代结束判断,下一个节点是 header 节点 public boolean hasNext() { return nextEntry != header; } public void remove() { if (lastReturned == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); LinkedHashMap.this.remove(lastReturned.key); lastReturned = null; expectedModCount = modCount; } // 获取下一个节点 Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextEntry == header) throw new NoSuchElementException(); Entry<K,V> e = lastReturned = nextEntry; nextEntry = e.after; return e; } } // key 迭代器 private class KeyIterator extends LinkedHashIterator<K> { public K next() { return nextEntry().getKey(); } } // value 迭代器 private class ValueIterator extends LinkedHashIterator<V> { public V next() { return nextEntry().value; } } // entry 迭代器 private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } Iterator<K> newKeyIterator() { return new KeyIterator(); } Iterator<V> newValueIterator() { return new ValueIterator(); } Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } /** * 添加节点,重写 HashMap 中的 addEntry() */ void addEntry(int hash, K key, V value, int bucketIndex) { super.addEntry(hash, key, value, bucketIndex); // 删除最老节点,也就是 header 的下一个节点 // 默认返回 false,不删除 Entry<K,V> eldest = header.after; if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } } void createEntry(int hash, K key, V value, int bucketIndex) { HashMap.Entry<K,V> old = table[bucketIndex]; Entry<K,V> e = new Entry<>(hash, key, value, old); table[bucketIndex] = e; // 添加到双向列表末尾 e.addBefore(header); size++; } /** * 默认返回 false,你可以自己重写它 * 比如 map 快满的时候,删除最老的节点 * * 下面这例子是 jdk 源码该方法注释中的例子 * <pre> * private static final int MAX_ENTRIES = 100; * * protected boolean removeEldestEntry(Map.Entry eldest) { * return size() > MAX_ENTRIES; * } * </pre> */ protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; } }
总结
- LinkedHashMap 数据存储和 HashMap 一样,数组+链表
- LinkedHashMap 的 Entry 继承自 HashMap.Entry<K, V>,多了 before 和 after 属性,用于形成双向循环链表
- 插入的时候添加到双向列表的末尾
- 迭代的时候是循环双向列表,而不是跟HashMap一样循环数组+链表,这样效率更高
- 多了访问顺序,默认是按插入顺序,如果是按访问顺序,那么每次put(修改) 和 get 的算一次访问,这时候就把该节点放到双向列表末尾
这篇关于LinkedHashMap 源码分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南