一、HashMap
1. 核心实现原理
- 数据结构:数组 + 链表/红黑树(JDK8+)
- 初始容量:默认16(
DEFAULT_INITIAL_CAPACITY)
- 负载因子:默认0.75f(
DEFAULT_LOAD_FACTOR)
- 扩容机制:当
size > capacity * loadFactor时扩容为2倍
2. 关键特性
transient Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
3. 工作流程
- 计算哈希:
(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
- 确定桶位:
(n - 1) & hash
- 处理冲突:
- 链表:遍历查找,JDK8尾插法
- 红黑树:当链表长度≥8且数组长度≥64时转换
- 扩容机制:
- 创建新数组(2倍原大小)
- 重新计算位置:
e.hash & (newCap - 1)
- JDK8优化:节点位置=原位置 或 原位置+原容量
4. 重要参数
| 参数 | 默认值 | 说明 |
|---|
| TREEIFY_THRESHOLD | 8 | 链表转红黑树阈值 |
| UNTREEIFY_THRESHOLD | 6 | 红黑树退化为链表阈值 |
| MIN_TREEIFY_CAPACITY | 64 | 最小树化容量 |
二、HashSet
1. 核心实现
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
2. 关键特性
三、HashTable
1. 核心实现
- 数据结构:数组 + 链表(无红黑树优化)
- 初始容量:默认11
- 负载因子:默认0.75
- 扩容机制:
newCapacity = (oldCapacity << 1) + 1
2. 线程安全实现
public synchronized V put(K key, V value) {
}
3. 与HashMap对比
| 特性 | HashTable | HashMap |
|---|
| 线程安全 | ✅ 方法级synchronized | ❌ |
| Null处理 | 不允许null键/值 | 允许一个null键,多个null值 |
| 哈希计算 | 直接使用key.hashCode() | 二次哈希(h = key.hashCode()) ^ (h >>> 16) |
| 继承体系 | 继承Dictionary类 | 实现AbstractMap接口 |
| 迭代器 | Enumerator(不支持fail-fast) | Iterator(支持fail-fast) |
四、对比总结
| 特性 | HashMap | HashSet | HashTable |
|---|
| 实现接口 | Map | Set | Map |
| 底层结构 | 数组+链表/红黑树 | HashMap | 数组+链表 |
| 线程安全 | ❌ | ❌ | ✅ |
| Null支持 | ✅ | ✅ | ❌ |
| 迭代顺序 | 不保证有序 | 不保证有序 | 不保证有序 |
| 初始容量 | 16 | 16 | 11 |
| 扩容倍数 | 2倍 | 依赖HashMap | 2倍+1 |
| 哈希冲突解决 | 链表/红黑树 | 同HashMap | 链表 |
| 性能 | 高 | 高 | 低(同步开销) |
| 推荐使用场景 | 单线程环境 | 元素去重 | 遗留系统维护 |
五、使用建议
- 首选HashMap:单线程环境下性能最优
- 线程安全替代:
ConcurrentHashMap 替代 HashTable
Collections.synchronizedMap() 包装HashMap
- HashSet适用场景:
Set<String> uniqueWords = new HashSet<>();
for (String word : words) {
uniqueWords.add(word);
}
- HashTable遗留问题:
- 性能瓶颈(全局锁)
- 已被
ConcurrentHashMap取代
注:JDK8+中HashMap的红黑树优化显著提升了哈希冲突严重时的查询效率(时间复杂度从O(n)优化到O(log n))。