当前位置: 首页 > news >正文

HashMap 底层原理详解

1. 核心数据结构

JDK 1.7 及之前数组 + 链表
JDK 1.8 及之后数组 + 链表/红黑树(链表长度 ≥8 时转红黑树,≤6 时退化为链表)

// JDK 1.8 的 Node 定义(链表节点)
static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next; // 链表指针
}

// TreeNode 定义(红黑树节点)
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;           // 颜色标识
}

2. 哈希函数设计

作用:将 Key 映射到数组索引,尽可能减少哈希冲突。
JDK 1.8 的优化

static final int hash(Object key) {
    int h;
    // 高16位与低16位异或,提升散列性
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

索引计算
index = (table.length - 1) & hash

  • 长度取模优化:哈希表容量为 2 的幂次时,(n-1) & hash 等效于 hash % n,但位运算更快。


3. put() 方法流程
  1. 计算哈希值:调用 hash(key)

  2. 初始化或扩容:若数组为空,调用 resize() 初始化(默认容量 16,负载因子 0.75)。

  3. 定位桶位置index = (n-1) & hash

  4. 处理哈希冲突

    • 链表插入

      • JDK 1.7:头插法(易导致死循环)。

      • JDK 1.8:尾插法(解决死循环问题)。

    • 树化处理:若链表长度 ≥8 且数组长度 ≥64,链表转红黑树。

  5. 覆盖或新增节点

    • Key 已存在:覆盖 Value,返回旧值。

    • Key 不存在:插入新节点,返回 null。

  6. 扩容检查:若元素总数 > 阈值(容量 × 负载因子),触发 resize()


4. 扩容机制(resize())

触发条件:元素数量超过阈值(容量 × 负载因子,默认 0.75)。
扩容流程

  1. 新容量计算:旧容量 × 2(保证容量始终为 2 的幂次)。

  2. 迁移元素

    • JDK 1.7:遍历旧数组,重新哈希每个元素到新数组(头插法)。

    • JDK 1.8:优化迁移逻辑,链表元素拆分为高位链和低位链(无需重新哈希):

      • 低位链:原索引位置

      • 高位链:原索引位置 + 旧容量

优化原理
由于新容量是旧容量的 2 倍,(newCap - 1) & hash 的结果仅取决于哈希值的第 log2(oldCap) 位是否为 1:

  • 若为 0 → 索引不变(低位链)。

  • 若为 1 → 索引 = 原索引 + 旧容量(高位链)。


5. 红黑树优化

树化条件

  • 链表长度 ≥ TREEIFY_THRESHOLD(默认 8)。

  • 数组长度 ≥ MIN_TREEIFY_CAPACITY(默认 64)。

退化条件

  • 红黑树节点数 ≤ UNTREEIFY_THRESHOLD(默认 6)。

优势

  • 链表查询复杂度 O(n),红黑树查询复杂度 O(logn),显著减少哈希冲突时的性能损耗。


6. 关键参数与默认值
参数默认值说明
DEFAULT_INITIAL_CAPACITY16默认初始容量
DEFAULT_LOAD_FACTOR0.75负载因子(扩容阈值 = 容量 × 负载因子)
TREEIFY_THRESHOLD8链表转红黑树的阈值
UNTREEIFY_THRESHOLD6红黑树退化为链表的阈值
MIN_TREEIFY_CAPACITY64允许树化的最小数组长度

7. 性能优化建议
  • 初始化容量:预估元素数量,避免频繁扩容(如预计存 1000 元素,初始容量设为 2048)。

  • 重写 hashCode() 和 equals():确保 Key 对象的哈希分布均匀且相等性判断准确。

  • 避免高频修改:多线程场景使用 ConcurrentHashMap


总结

  • 核心结构:数组 + 链表/红黑树,动态扩容优化性能。

  • 哈希设计:高位异或、位运算取模、红黑树优化冲突。

  • 线程安全:非线程安全,需使用替代方案。

  • 实战技巧:合理初始化容量、重写哈希方法、避免并发操作。


http://www.dtcms.com/a/113275.html

相关文章:

  • C++学习之LINUX网络编程-套接字通信基础
  • JWT认证服务
  • [MySQL初阶]MySQL(9)事务机制
  • 基于springboot+vue的二手车交易系统
  • Supervisor的安装和使用
  • 0101安装matplotlib_numpy_pandas-报错-python
  • Business English Certificates (BEC) 高频词汇学习
  • 将MATLAB神经网络数据转换为C/C++进行推理计算
  • Linux网络状态监控利器:netstat与ping命令详解
  • Java的Selenium的特殊元素操作与定位之select下拉框
  • RocketMQ初认识
  • C,C++语言缓冲区溢出的产生和预防
  • 【2012】【论文笔记】太赫兹波在非磁化等离子体——
  • 【国产突围!致远电子ZXDoc如何打破Vector垄断,成为新能源车研发“神器”?】
  • Xshell Plus 6下载与安装
  • 【机器学习】机器学习工程实战-第4章 特征工程
  • LabVIEW商业软件开发注意问题
  • C语言-基础语法学习
  • 【Linux系统】linux下的软件管理
  • 大数据技术发展与应用趋势分析
  • `use_tempaddr` 和 `temp_valid_lft ` 和 `temp_prefered_lft ` 笔记250405
  • web性能检测工具lighthouse
  • k8s 1.23升级1.24
  • JavaSE基础——第六章 类与对象(二)
  • 使用dockerbuildx在x86机器上构建arm版docker镜像
  • 神经网络基础
  • 嵌入式AI简介
  • java面向对象 - 封装、继承和多态
  • 浅谈ai - Activation Checkpointing - 时间换空间
  • HANA如何在存储过程里执行动态SQL