在 Java 编程中,HashMap 是一种极为常用的数据结构,它提供了高效的键值对存储与检索功能。然而,其背后的实现原理却鲜为人知。本文将深入剖析 HashMap 的底层架构与运作机制,带您领略其精妙之处。
一、底层数据结构
HashMap 的底层数据结构主要由数组、链表和红黑树组成。
数组:作为 HashMap 的核心存储空间,数组中的每个元素称为桶(bucket)。每个桶可以存储一个或多个键值对。
链表:在 Java 7 及之前的版本中,当多个键映射到同一个数组索引时,这些键值对会以链表的形式存储在同一个桶中。链表中的每个节点都包含一个键值对以及指向下一个节点的引用。
红黑树:从 Java 8 开始,为了优化链表过长时的查找效率,HashMap 引入了红黑树。当一个桶中的链表长度超过 8 且数组长度大于 64 时,链表会被转换成红黑树。红黑树是一种自平衡二叉查找树,其查找时间复杂度为 O(log n),相较于链表的 O(n) 有显著提升。
二、哈希函数与冲突解决
HashMap 使用哈希函数来计算键的哈希码,并将哈希码映射到数组的索引位置。哈希函数通常采用取模运算的方式,以确保哈希码均匀地分布在数组的各个位置。
当多个键映射到同一个索引位置时,就会发生哈希冲突。HashMap 采用了链地址法来解决哈希冲突,即将冲突的键值对以链表的形式存储在同一个桶中。从 Java 8 开始,当链表长度超过一定阈值时,链表会被转换成红黑树以提高查找效率。
三、数据操作流程
插入键值对
- 计算键的哈希码。
- 根据哈希码计算出要存储的桶的索引位置。
- 如果该位置为空,则直接将键值对放入该位置;如果该位置已经有键值对存在,则将新的键值对添加到链表或红黑树中。
获取键值对
- 计算键的哈希码。
- 根据哈希码计算出存储位置的索引。
- 在该位置的链表或红黑树中查找键对应的值,并返回结果。
删除键值对
- 计算键的哈希码。
- 根据哈希码计算出存储位置的索引。
- 在该位置的链表或红黑树中查找并删除键值对。
四、扩容机制
为了保证 HashMap 的性能,当 HashMap 中的元素数量达到一定阈值时,会触发扩容操作。扩容时,HashMap 会创建一个新的数组,其大小是原数组的两倍,并将原数组中的所有元素重新哈希并存储到新数组中。这种扩容机制可以确保 HashMap 在不断插入新元素时仍能保持较高的查找效率。