您好,登录后才能下订单哦!
# 如何深入解读LinkedHashMap原理和源码
## 一、LinkedHashMap概述
LinkedHashMap是Java集合框架中一个重要的实现类,它继承自HashMap,在HashMap的基础上通过维护一个双向链表来保证元素的插入顺序或访问顺序。与HashMap的纯哈希表结构不同,LinkedHashMap通过结合哈希表和链表的优势,提供了可预测的迭代顺序。
### 1.1 核心特性
- **有序性**:默认保持插入顺序(accessOrder=false),可配置为访问顺序(LRU模式)
- **性能平衡**:查找效率O(1),略低于HashMap但远优于TreeMap
- **继承关系**:`java.util.LinkedHashMap` → `java.util.HashMap` → `java.util.AbstractMap`
### 1.2 典型应用场景
- 需要保持插入顺序的缓存系统
- 实现LRU(最近最少使用)缓存策略
- 需要有序键值对且不频繁修改的场景
## 二、数据结构解析
### 2.1 底层存储结构
```java
// 继承HashMap的Node并添加双向指针
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
LinkedHashMap在HashMap的数组+链表/红黑树结构基础上,额外维护了一个双向链表:
- 头节点:transient LinkedHashMap.Entry<K,V> head
- 尾节点:transient LinkedHashMap.Entry<K,V> tail
插入顺序(默认):
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false; // 关键参数
}
访问顺序(LRU):
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder; // 设置为true
}
访问节点时的顺序维护(get()
方法):
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder) // 如果为访问顺序模式
afterNodeAccess(e); // 将被访问节点移到链表尾部
return e.value;
}
afterNodeAccess()
方法实现:
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;
// 从链表中移除节点p
LinkedHashMap.Entry<K,V> 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;
// 将p插入到链表尾部
if (last == null) head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
插入节点时的钩子方法(覆盖HashMap的newNode()
):
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<>(hash, key, value, e);
linkNodeLast(p); // 将新节点链接到链表尾部
return p;
}
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;
}
}
删除节点后的处理(afterNodeRemoval()
):
void afterNodeRemoval(Node<K,V> e) {
LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e;
LinkedHashMap.Entry<K,V> 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;
}
通过覆盖removeEldestEntry()
方法实现:
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false; // 默认不删除
}
// 典型LRU缓存实现示例
final int MAX_ENTRIES = 100;
LinkedHashMap<Integer, String> cache = new LinkedHashMap<>(16, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
};
abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
LinkedHashIterator() {
next = head; // 从头节点开始
expectedModCount = modCount;
}
final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;
next = e.after; // 通过after指针遍历
return e;
}
// ... 其他方法省略
}
KeyIterator
:继承LinkedHashIterator
实现Iterator<K>
ValueIterator
:继承LinkedHashIterator
实现Iterator<V>
EntryIterator
:继承LinkedHashIterator
实现Iterator<Map.Entry<K,V>>
操作 | HashMap | LinkedHashMap | TreeMap |
---|---|---|---|
get() | O(1) | O(1) | O(log n) |
put() | O(1) | O(1) | O(log n) |
remove() | O(1) | O(1) | O(log n) |
迭代 | 无序 | O(n)有序 | O(n)有序 |
与HashMap相同,LinkedHashMap非线程安全,多线程环境下应该:
Map<String, Object> safeMap = Collections.synchronizedMap(
new LinkedHashMap<>(16, 0.75f, true));
预估容量避免频繁扩容:
// 预期存储100元素,0.75负载因子
new LinkedHashMap<>( (int)(100/0.75)+1, 0.75f )
访问顺序模式下的并发修改异常:
// 迭代过程中访问元素会触发modCount变化
for(Key key : map.keySet()) {
map.get(key); // 抛出ConcurrentModificationException
}
LinkedHashMap通过精巧的双向链表设计,在几乎不损失HashMap性能优势的前提下实现了有序性。理解其实现原理有助于:
扩展思考:如何基于LinkedHashMap实现一个支持过期时间的缓存系统?(提示:结合定时任务清理机制) “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。