HashMap源码解析

📚 核心目录


设计哲学与版本演进

 

JDK1.2

数组+链表

JDK1.5引入ConcurrentHashMap

JDK1.8树化优化

红黑树引入

尾插法替代头插法

hash算法优化


基础数据结构剖析

存储单元结构(JDK1.8+)

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;    // 扰动后的哈希值
    final K key;
    V value;
    Node<K,V> next;    // 链表指针
  
    // 树节点扩展
    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
        TreeNode<K,V> parent;
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // 实现快速拆解
        boolean red;
    }
}

哈希函数实现奥秘

JDK1.8扰动算法

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

索引计算原理

// (n-1) & hash 代替模运算(要求n必须是2的幂)
index = (table.length - 1) & hash;

核心方法源码解读

putVal方法流程图

 

开始

计算key哈希

桶是否空?

新建节点存入

是树节点?

红黑树插入

遍历链表

找到相同key?

覆盖value

尾部插入新节点

长度>=8?

转换为红黑树

树化阈值控制逻辑

static final int TREEIFY_THRESHOLD = 8;       // 链表转树阈值
static final int UNTREEIFY_THRESHOLD = 6;     // 树转链表阈值
static final int MIN_TREEIFY_CAPACITY = 64;   // 最小树化容量

扩容机制深度分析

resize() 核心步骤

  1. 计算新容量(oldCap << 1)

  2. 创建新数组

  3. 数据迁移:

    • 单节点:直接rehash

    • 链表:拆分为高低位链(基于高位bit)

    • 红黑树:拆分为红黑树结构或退化为链表

高低位拆分原理

// JDK1.8扩容优化关键代码
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
next = e.next;
if ((e.hash & oldCap) == 0) {  // 判断高位bit
    if (loTail == null) loHead = e;
    else loTail.next = e;
    loTail = e;
} else {
    if (hiTail == null) hiHead = e;
    else hiTail.next = e;
    hiTail = e;
}

线程安全问题全解

经典死链问题(JDK1.7)

 

线程AEntry链线程B注意系统执行头插法同时修改next指针产生环形链表导致死循环线程AEntry链线程B注意系统


并发优化方案对比

线程安全实现方案

方案原理适用场景
Collections.synchronizedMap方法级synchronized锁低并发场景
ConcurrentHashMap分段锁+CAS高并发读写
ReadWriteLock读写分离锁读多写少场景
Hashtable全表锁已淘汰,不推荐使用

性能调优实践指南

初始化参数最佳实践

// 预期存储100个元素,负载因子0.75
int expectedSize = 100;
float loadFactor = 0.75f;
int initialCapacity = (int) Math.ceil(expectedSize / loadFactor);
​
Map<String, Object> optimizedMap = new HashMap<>(initialCapacity, loadFactor);

高频面试题破解

Q1:为什么树化阈值是8?

  • 泊松分布统计:哈希冲突达到8的概率不足千万分之一

  • 平衡链表与树的性能:树查找O(logn) vs 链表O(n)

Q2:HashMap为什么使用红黑树?

  • 相较于AVL树,红黑树牺牲少许平衡性换取更快的插入/删除速度

  • 在哈希冲突严重的场景下保证基础查询性能


总结与思考

  1. 数据结构选择:为什么HashMap不直接使用红黑树存储?

  2. 版本演进:为什么JDK1.8将头插法改为尾插法?

  3. 并发安全:如何设计高并发场景下的安全哈希表?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值