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

HashMap 的底层原理

文章目录

    • 核心数据结构
    • 核心参数
    • 核心操作原理
      • 哈希计算与数组索引
      • 插入(put)操作
      • 查询(get)操作
      • 扩容(resize)操作
      • 链表转红黑树(treeifyBin)
      • 红黑树退化为链表(untreeify)
    • 关键特性
    • 总结

HashMap 的底层原理(Java 8 版本)

HashMap 是 Java 中最常用的基于哈希表的Map实现,它存储键值对(key-value),具有 O(1) 平均时间复杂度 的查询、插入和删除性能。其底层实现涉及 数组 + 链表 + 红黑树 的结构,并在 Java 8 中进行了优化(链表转红黑树)。下面详细分析其核心机制:


核心数据结构

HashMap 的底层是一个 Node<K,V>[] table 数组(默认大小 16),每个元素是一个链表或红黑树的节点:

transient Node<K,V>[] table; // 存储键值对的数组
  • Node 类(链表节点):

    static class Node<K,V> implements Map.Entry<K,V> {final int hash;      // key 的哈希值final K key;         // 键V value;             // 值Node<K,V> next;      // 下一个节点(链表结构)
    }
    
  • TreeNode 类(红黑树节点,Java 8 新增):

    • 当链表长度超过阈值(默认 8)时,链表会转换为红黑树,提升查询效率(从 O(n) → O(log n))。

核心参数

参数默认值作用
initialCapacity16初始数组大小(必须是 2 的幂次方)。
loadFactor0.75负载因子,决定扩容时机(容量 * loadFactor 触发扩容)。
threshold12扩容阈值 = 容量 * loadFactor(默认 16 * 0.75 = 12)。
TREEIFY_THRESHOLD8链表长度超过此值时,转换为红黑树。
UNTREEIFY_THRESHOLD6红黑树节点数减少到此值时,退化为链表。

核心操作原理

哈希计算与数组索引

  • 计算 key 的哈希值

    • 先调用key.hashCode(),再通过hash()方法扰动哈希值(减少哈希冲突):

      static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); // 高16位与低16位异或
      }
      
    • 扰动函数的作用:将高位哈希值参与低位运算,避免哈希冲突(如 key1.hashCode() 和 key2.hashCode()的高位不同但低位相同的情况)。

  • 计算数组索引

    • 使用(n - 1) & hash计算索引(n是数组长度,必须是 2 的幂次方):

      int index = (table.length - 1) & hash; // 等价于 index = hash % n(但位运算更快)
      
    • 为什么用 & 而不是 %?

      • n 是 2 的幂次方时,(n - 1) & hash 等价于 hash % n,但位运算效率更高。

插入(put)操作

  1. 计算 key 的哈希值和数组索引
  2. 检查数组是否为空或长度为 0:
    • 如果是,先调用 resize()扩容(默认初始容量 16)。
  3. 检查当前索引位置是否有节点:
    • 无节点:直接插入新 Node。
    • 有节点:
      • 如果 key 已存在:更新 value(equals() 比较 key)。
      • 如果 key 不存在:
        • 链表结构:插入到链表末尾。
        • 链表长度 ≥ 8:转换为红黑树(TREEIFY_THRESHOLD=8)。
  4. 检查是否需要扩容:
    • 如果当前元素数量 ≥ threshold,调用 resize()`扩容(容量翻倍)。

查询(get)操作

  1. 计算 key 的哈希值和数组索引
  2. 检查当前索引位置是否有节点:
    • 无节点:返回 null。
    • 有节点:
      • 链表结构:遍历链表,用 equals() 比较 key。
      • 红黑树结构:调用红黑树的查找方法(O(log n))。

扩容(resize)操作

  • 触发条件:元素数量 ≥ threshold(默认 16 * 0.75 = 12)。
  • 扩容过程:
    1. 新容量 = 旧容量 × 2(必须是 2 的幂次方)。
    2. 遍历旧数组的所有节点,重新计算哈希值和索引:
      • 旧容量为 2 的幂次方时,节点的新索引要么不变,要么是 旧索引 + 旧容量(利用哈希值的高位信息减少重新计算)。
    3. 如果链表长度 ≥ 8,转换为红黑树。

链表转红黑树(treeifyBin)

  • 条件:链表长度 ≥ TREEIFY_THRESHOLD=8 数组长度 ≥ MIN_TREEIFY_CAPACITY=64。
  • 作用:提升查询效率(从 O(n) → O(log n))。

红黑树退化为链表(untreeify)

  • 条件:红黑树节点数 ≤ UNTREEIFY_THRESHOLD=6。
  • 作用:减少内存占用(红黑树比链表占用更多空间)。

关键特性

  1. 哈希冲突处理:
    • 链地址法:相同哈希值的 key 存储在同一个链表或红黑树中。
    • 扰动函数:减少哈希冲突的概率。
  2. 扩容机制:
    • 容量翻倍,重新计算所有节点的索引(利用(n - 1) & hash的特性减少计算量)。
  3. 红黑树优化:
    • 链表过长时转换为红黑树,提升查询效率(Java 8 新增)。
  4. 线程不安全:
    • 多线程环境下可能导致死循环或数据丢失(需用 ConcurrentHashMap 或 Collections.synchronizedMap)。

总结

HashMap 的底层是基于数组 + 链表 + 红黑树的哈希表实现,其核心原理包括:

  1. 哈希计算:通过 key.hashCode() 和扰动函数计算哈希值,再用 (n - 1) & hash计算数组索引。
  2. 插入操作:先检查数组是否为空,再检查当前索引是否有节点,若无则插入;若有则更新或插入链表/红黑树。
  3. 扩容机制:当元素数量超过 容量 * loadFactor 时,容量翻倍并重新计算所有节点的索引。
  4. 链表转红黑树:当链表长度 ≥ 8 且数组长度 ≥ 64 时,链表转换为红黑树以提升查询效率。
  5. 线程不安全:多线程环境下需用 ConcurrentHashMap 替代。
    其设计目标是平衡查询效率、内存占用和扩容成本。
http://www.xdnf.cn/news/778501.html

相关文章:

  • 小白的进阶之路系列之十二----人工智能从初步到精通pytorch综合运用的讲解第五部分
  • 网络安全问题及对策研究
  • Java面试八股--08-数据结构和算法篇
  • JavaWeb是什么?总结一下JavaWeb的体系
  • MQTTX连接阿里云的物联网配置
  • Linux 下 ChromeDriver 安装
  • 70道Hive高频题整理(附答案背诵版)
  • Express教程【006】:使用Express写接口
  • “草台班子”的成长路径分析
  • 基于InternLM的情感调节大师FunGPT
  • Cilium动手实验室: 精通之旅---1.Getting Started with Cilium
  • 深度学习学习率调度器指南:PyTorch 四大 scheduler 对决
  • # 将本地UI生成器从VLLM迁移到DeepSeek API的完整指南
  • iOS 应用如何防止源码与资源被轻易还原?多维度混淆策略与实战工具盘点(含 Ipa Guard)
  • 深入浅出:Oracle 数据库 SQL 执行计划查看详解(1)——基础概念与查看方式
  • 蛋白质结构预测软件openfold介绍
  • 【请关注】MySQL 中常见的加锁方式及各类锁常见问题及对应的解决方法
  • macos常见且应该避免被覆盖的系统环境变量(避免用 USERNAME 作为你的自定义变量名)
  • 数据结构:递归:自然数之和
  • MYSQL 高级 SQL 技巧
  • 虚拟线程与消息队列:Spring Boot 3.5 中异步架构的演进与选择
  • 从零打造AI面试系统全栈开发
  • 字节新出的MCP应用DeepSearch,有点意思。
  • 基于大模型的短暂性脑缺血发作(TIA)全流程预测与干预系统技术方案
  • forEach不能用return中断循环,还是会走循环外的逻辑
  • idea不识别lombok---实体类报没有getter方法
  • 【计算机网络】第七章 运输层
  • 阿里云无影云桌面深度测评
  • GLIDE论文阅读笔记与DDPM(Diffusion model)的原理推导
  • 调用.net DLL让CANoe自动识别串口号