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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
| /**
* 模拟Java 8 HashMap的核心实现
*/
public class MyHashMap<K, V> {
// 红黑树阈值
static final int TREEIFY_THRESHOLD = 8; // 链表转红黑树阈值
static final int UNTREEIFY_THRESHOLD = 6; // 红黑树转链表阈值
static final int MIN_TREEIFY_CAPACITY = 64;
// 哈希表节点
static class Node<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;
Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
private Node<K, V>[] table;
private int size;
private int threshold; // 扩容阈值
private float loadFactor = 0.75f;
/**
* HashMap的哈希函数(扰动函数)
*/
static final int hash(Object key) {
int h;
// null键映射到0,其他键做高16位与低16位异或扰动
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* 确定在数组中的索引位置
*/
private int indexFor(int hash, int length) {
return hash & (length - 1); // 等价于 hash % length,但更高效
}
/**
* 插入或更新键值对
*/
public V put(K key, V value) {
return putVal(hash(key), key, value);
}
private V putVal(int hash, K key, V value) {
Node<K, V>[] tab = table;
int n = (tab == null) ? 0 : tab.length;
// 初始化或扩容
if (tab == null || n == 0) {
tab = resize();
n = tab.length;
}
int index = indexFor(hash, n);
Node<K, V> p = tab[index];
if (p == null) {
// 位置为空,直接插入
tab[index] = new Node<>(hash, key, value, null);
} else {
Node<K, V> e = null;
// 检查第一个节点
if (p.hash == hash && Objects.equals(p.key, key)) {
e = p;
} else {
// 遍历链表或红黑树
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
// 链表末尾,插入新节点
p.next = new Node<>(hash, key, value, null);
// 检查是否需要树化
if (binCount >= TREEIFY_THRESHOLD - 1) {
treeifyBin(tab, index);
}
break;
}
// 找到相同键
if (e.hash == hash && Objects.equals(e.key, key)) {
break;
}
p = e;
}
}
// 更新已存在的键
if (e != null) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
++size;
if (size > threshold) {
resize();
}
return null;
}
/**
* 扩容操作
*/
@SuppressWarnings("unchecked")
private Node<K, V>[] resize() {
Node<K, V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
newCap = oldCap << 1; // 容量翻倍
newThr = oldThr << 1; // 阈值翻倍
} else {
newCap = 16; // 默认初始容量
newThr = (int)(newCap * loadFactor);
}
threshold = newThr;
Node<K, V>[] newTab = new Node[newCap];
table = newTab;
// 重新哈希所有元素
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K, V> e = oldTab[j];
if (e != null) {
oldTab[j] = null;
if (e.next == null) {
// 单个节点
newTab[indexFor(e.hash, newCap)] = e;
} else {
// 链表需要分裂为两条链表
splitChain(e, newTab, j, oldCap);
}
}
}
}
System.out.printf("HashMap扩容: %d -> %d%n", oldCap, newCap);
return newTab;
}
/**
* 链表分裂(HashMap 1.8优化)
*/
private void splitChain(Node<K, V> head, Node<K, V>[] newTab, int oldIndex, int oldCap) {
Node<K, V> loHead = null, loTail = null; // 低位链表
Node<K, V> hiHead = null, hiTail = null; // 高位链表
Node<K, V> next;
Node<K, V> e = head;
do {
next = e.next;
// 根据扩容位判断分到哪条链表
if ((e.hash & oldCap) == 0) {
if (loTail == null) {
loHead = e;
} else {
loTail.next = e;
}
loTail = e;
} else {
if (hiTail == null) {
hiHead = e;
} else {
hiTail.next = e;
}
hiTail = e;
}
} while ((e = next) != null);
// 放置到新数组中
if (loTail != null) {
loTail.next = null;
newTab[oldIndex] = loHead; // 原位置
}
if (hiTail != null) {
hiTail.next = null;
newTab[oldIndex + oldCap] = hiHead; // 原位置+旧容量
}
}
private void treeifyBin(Node<K, V>[] tab, int index) {
// 简化实现,实际会转换为红黑树
System.out.println("位置 " + index + " 的链表过长,转换为红黑树");
}
}
|