1. 基本结构
java
public class LinkedHashMap<K,V> extends HashMap<K,V> {
// 双向链表的头尾节点
transient LinkedHashMap.Entry<K,V> head; // 头节点
transient LinkedHashMap.Entry<K,V> tail; // 尾节点
// 节点定义
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);
}
}
}
2. 数据结构图解
graph LR
A[HashMap数组] --> B[链表/红黑树]
C[双向链表] --> D[按插入顺序]
subgraph "HashMap结构"
A --> E[桶0]
A --> F[桶1]
A --> G[桶2]
end
subgraph "双向链表"
H[头节点] --> I[节点1]
I --> J[节点2]
J --> K[尾节点]
K --> J
J --> I
I --> H
end
3. 插入过程
java
// 1. 继承自HashMap的put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
// 2. 在节点插入后,会调用afterNodeInsertion方法
void afterNodeInsertion(boolean evict) {
LinkedHashMap.Entry<K,V> first;
// 维护插入顺序
if (evict && (first = head) != null) {
removeEldestEntry(first);
}
}
4. 实际例子
java
LinkedHashMap<String, String> map = new LinkedHashMap<>();
// 1. 插入元素
map.put("A", "1"); // 第一个
map.put("B", "2"); // 第二个
map.put("C", "3"); // 第三个
// 2. 遍历 - 会按插入顺序输出
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 输出:
// A:1
// B:2
// C:3
5. 两种结构的配合
graph TD
A[LinkedHashMap] --> B[HashMap结构]
A --> C[双向链表]
B --> D[快速查找]
C --> E[维护顺序]
subgraph "职责分工"
D --> F[O1时间复杂度访问]
E --> G[记录插入/访问顺序]
end
6. 特殊功能
6.1 按访问顺序排序
java
// 创建按访问顺序排序的LinkedHashMap
LinkedHashMap<String, String> accessMap =
new LinkedHashMap<>(16, 0.75f, true);
accessMap.put("A", "1");
accessMap.put("B", "2");
accessMap.put("C", "3");
accessMap.get("A"); // 访问A
// 现在A会移到最后
// 遍历顺序:B -> C -> A
7. LRU缓存实现
java
class LRUCache<K,V> extends LinkedHashMap<K,V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true); // 按访问顺序
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return size() > capacity; // 超过容量则移除最老的
}
}
8. 实际应用场景
java
// 1. 需要记录插入顺序
LinkedHashMap<String, User> userMap = new LinkedHashMap<>();
userMap.put("user1", new User("张三"));
userMap.put("user2", new User("李四"));
// 遍历时保证按插入顺序
// 2. LRU缓存
LRUCache<String, Data> cache = new LRUCache<>(100);
cache.put("key1", data1);
cache.get("key1"); // 访问会更新顺序
9. 性能特点
markdown
1. 查找性能:O(1),继承自HashMap
2. 插入性能:O(1),但比HashMap略慢(需要维护链表)
3. 内存消耗:比HashMap大(额外的链表指针)
10. 总结配合方式
-
HashMap 负责:
- 存储节点
- 快速查找
- 哈希计算
-
双向链表负责:
- 维护顺序
- 快速插入/删除
- 迭代顺序
就像是:
- HashMap是一个仓库(负责存储)
- 双向链表是一个记录本(负责记录顺序)
- 两者配合提供了"有序的Map"功能
这种组合让 LinkedHashMap 既拥有了 HashMap 的高效查找,又能维护元素的顺序,是一个非常精妙的设计。