HashMap是Java集合框架中的核心类,基于哈希表实现键值对(Key-Value)存储,提供O(1)时间复杂度的快速查找。以下从数据结构、哈希机制、冲突解决、扩容策略等角度详细解析其实现原理(基于Java 8)。
transient Node<K,V>[] table; // 哈希桶数组
static class Node<K,V> { // 链表节点
final int hash;
final K key;
V value;
Node<K,V> next;
}
static final class TreeNode<K,V> extends Node<K,V> { // 红黑树节点
TreeNode<K,V> parent;
TreeNode<K,V> left;
TreeNode<K,V> right;
}
计算键(Key)的哈希值,分散分布避免碰撞:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
key.hashCode()
获取原始哈希值。index = (table.length - 1) & hash
(等价于取模运算,但效率更高)。当不同Key的hash
值计算到同一桶索引时:
Node
链表连接。put()
)public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
hash
。resize()
初始化(默认长度16)。i = (n-1) & hash
。size > threshold
(阈值 = 容量 × 负载因子)。get()
)public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
equals()
比较Key值。resize()
)当元素数量超过阈值(threshold = capacity * loadFactor
)时触发:
final Node<K,V>[] resize() {
// 1. 计算新容量:旧容量 × 2
// 2. 创建新桶数组(长度翻倍)
// 3. 迁移元素(重哈希):
for (Node<K,V> e : oldTable) {
while (e != null) {
// 计算新索引:e.hash & (newCap-1)
// 迁移链表或拆分红黑树
}
}
}
i
的元素,新索引只能是i
或i + oldCap
。参数 | 默认值 | 说明 |
---|---|---|
初始容量(capacity ) |
16 | 桶数组初始长度(建议设为2的幂) |
负载因子(loadFactor ) |
0.75 | 扩容阈值比例(空间与时间的权衡) |
树化阈值(TREEIFY_THRESHOLD ) |
8 | 链表长度≥8时转为红黑树 |
退化阈值(UNTREEIFY_THRESHOLD ) |
6 | 红黑树节点≤6时退化为链表 |
最小树化容量(MIN_TREEIFY_CAPACITY ) |
64 | 桶数组≥64时才允许树化 |
ConcurrentHashMap
:分段锁(Java 7)或CAS + synchronized(Java 8)。Collections.synchronizedMap()
:全局锁包装类。HashMap通过哈希函数、链地址法和动态扩容实现高效键值存储:
最佳实践:预估数据量设置初始容量(避免频繁扩容),重写
hashCode()
和equals()
确保键对象正确性。