导读:本期聚焦于小伙伴创作的《Java中HashMap的底层数据结构是什么,数组+链表+红黑树是如何配合工作的》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Java中HashMap的底层数据结构是什么,数组+链表+红黑树是如何配合工作的》有用,将其分享出去将是对创作者最好的鼓励。

Java中的HashMap是基于哈希表实现的键值对存储集合,它的底层核心数据结构由数组、链表和红黑树三部分组成,这种设计既保证了大部分场景下的查询效率,也能应对哈希冲突带来的性能问题。

Java中HashMap的底层数据结构是什么,数组+链表+红黑树是如何配合工作的

HashMap底层结构的核心组成

1. 数组(Node数组)

HashMap底层首先是一个名为table的数组,数组的每个位置被称为一个桶(Bucket),桶的初始默认长度是16,长度始终保持为2的幂次方,这样可以通过位运算快速计算元素应该存放的桶位置。数组的类型是Node,这是HashMap内部定义的静态类,用来存储键值对数据。

Node类的基础结构如下:

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;    // 键的哈希值
    final K key;       // 键
    V value;           // 值
    Node<K,V> next;    // 指向下一个节点的指针,用于链表结构
    // 构造方法和其他方法省略
}

2. 链表

当两个不同的键通过哈希计算后得到相同的桶索引时,就会产生哈希冲突。HashMap采用链地址法解决哈希冲突,也就是在同一个桶位置下,用链表把多个冲突的Node节点连接起来。链表的存在避免了哈希冲突时数据丢失的问题,但是链表的查询时间复杂度是O(n),如果链表过长会影响HashMap的性能。

3. 红黑树

为了优化长链表的查询效率,当某个桶下的链表长度达到阈值(默认是8),并且当前HashMap的数组长度大于等于64时,这个桶下的链表就会转换为红黑树。红黑树是一种自平衡的二叉查找树,查询时间复杂度为O(log n),比长链表的查询效率更高。如果后续红黑树中的节点数量减少到6及以下,红黑树又会退化为链表。

三者的配合工作流程

当我们往HashMap中插入一个键值对时,整体的处理流程如下:

  • 第一步:计算键的哈希值,通过(n - 1) & hash的位运算得到该键值对应该存放的桶索引,其中n是数组的长度。
  • 第二步:检查对应桶位置是否为空,如果为空,直接把新的Node节点放到这个桶位置。
  • 第三步:如果桶位置不为空,说明发生了哈希冲突,判断当前桶位置是链表还是红黑树:
    • 如果是链表,遍历链表,如果找到键相同的节点就覆盖值,否则在链表尾部插入新节点,插入后如果链表长度达到8,就触发链表转红黑树的逻辑。
    • 如果是红黑树,就按照红黑树的插入规则插入新的节点。
  • 第四步:插入完成后,检查HashMap的元素数量是否超过阈值(数组长度 * 负载因子,默认负载因子是0.75),如果超过就触发数组扩容,重新计算每个节点的桶位置。

关键参数说明

HashMap中和底层结构相关的重要参数如下:

参数名默认值作用说明
DEFAULT_INITIAL_CAPACITY16数组的默认初始长度
DEFAULT_LOAD_FACTOR0.75负载因子,决定数组扩容的时机
TREEIFY_THRESHOLD8链表转红黑树的阈值
UNTREEIFY_THRESHOLD6红黑树退化为链表的阈值
MIN_TREEIFY_CAPACITY64链表转红黑树时数组的最小长度要求

简单代码示例

下面是一段简单的HashMap插入和查询的示例代码,帮助理解底层结构的作用:

import java.util.HashMap;

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建HashMap实例,底层数组初始长度为16
        HashMap<String, Integer> map = new HashMap<>();
        // 插入键值对,会根据键的哈希值计算桶位置,若有冲突则形成链表或红黑树
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("orange", 3);
        // 查询键值对,通过哈希定位桶,再在桶内的链表或红黑树中查找
        Integer value = map.get("apple");
        System.out.println(value); // 输出1
    }
}

常见疑问解答

为什么数组长度必须是2的幂次方?

因为计算桶索引时用的是(n - 1) & hash,当n是2的幂次方时,n-1的二进制所有位都是1,位运算的结果和取模运算结果一致,但是位运算的效率比取模运算高很多,同时也能保证计算出的桶索引分布更均匀。

为什么链表转红黑树的阈值是8?

这是根据泊松分布计算得出的结果,在理想的哈希分布下,链表长度达到8的概率非常低,所以选择8作为阈值,既能避免频繁转换结构带来的性能损耗,也能在链表过长时及时优化查询效率。

扩容时为什么所有节点要重新计算桶位置?

因为扩容后数组的长度n发生了变化,原来的(n - 1) & hash计算结果也会变化,所以需要重新计算每个节点对应的新桶位置,保证节点分布到新的数组中。

HashMap数组_链表_红黑树Java集合哈希表修改时间:2026-06-20 06:36:33

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。