找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3551

积分

0

好友

479

主题
发表于 2 小时前 | 查看: 2| 回复: 0

哈希表介绍

哈希表,也常被称为散列表,这种数据结构精妙地实现了键(Key)与值(Value)之间的映射。只要你提供一个 Key,它就能以接近 O(1) 的时间复杂度,高效地检索出对应的 Value。这背后到底是怎么实现的呢?

其核心在于哈希函数。哈希函数能将任意长度的输入数据,通过特定的哈希算法,转换成固定长度的输出,这个输出值就是哈希值或散列值。哈希表正是依靠哈希函数,完成了从 Key 到数组下标的转换,从而构建起高效的查询基础。

哈希表的读写操作与扩容

1. 写操作

在哈希表中插入新的键值对,也就是写操作,具体是如何实现的?我们把它拆解为两步:

  • 第一步:通过哈希函数,将 Key 转换为一个数组下标。
  • 第二步:检查这个下标对应的数组位置。如果该位置是空的,没有任何元素,那么这个键值对就会被直接填充进去。

不过,现实往往不如预想中顺利。随着数据量增加,不同的 Key 可能会计算出相同的数组下标,这种情况就是我们常说的 哈希冲突。别担心,解决冲突的经典方法主要有两种:开放寻址法和链表法。

  • 开放寻址法:思路很直接,当发现一个位置已经被占用时,就从这个位置开始向后探测,逐一检查,直到找到一个空的槽位,然后将键值对存放进去。
  • 链表法:这种方法更为常见。如果新键值对被分配到了一个已被占用的位置,它不会去别处寻找空位,而是直接以当前位置为链表的头节点,将自己作为新节点“挂”在这条链的末尾。每个数组槽位都关联一条链表,所有哈希冲突的元素都在这条链表上排排坐。

2. 读操作

读操作与写操作相对应,过程相对直观:

  • 第一步:再次通过哈希函数,依据 Key 找到它本应归属的数组下标。
  • 第二步:定位到那个下标位置。如果该位置存储了多个元素(即存在链表),那就不只是简单地取第一个,而是需要顺着链表逐个向下查找,直到找到那个与 Key 完全匹配的节点,并返回它的 Value。

3. 扩容

为什么要扩容?想象一下,如果链表变得越来越长,沿着链表逐个查找的效率自然会下降,这显然不符合我们追求 O(1) 效率的初衷。因此,在合适的时机对哈希表进行扩容就变得至关重要。扩容的决策主要由两个因素共同决定:

  • Capacity:哈希表当前的总长度,也就是数组的槽位数量。
  • LoadFactor:哈希表的负载因子,这是一个用于衡量哈希表填充程度的关键比例。

扩容的触发条件,通常满足以下公式即可:

HashMap.Size >= Capacity × LoadFactor

当哈希表中的元素数量达到或超过当前容量与负载因子的乘积时,数组就会进行一次扩容。扩容后,通常需要重新计算所有现存元素的位置(即 Rehashing),将它们均匀地分布到新的、更大的数组中,以此来维持高效的查询性能。




上一篇:阿里巴巴Java面试现场还原:ConcurrentHashMap连环追问,我如何扛过高并发拷问
下一篇:离职4个月被前领导半夜要求改方案,答应8点交付后拉黑,我错了吗?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-5-3 02:10 , Processed in 0.811359 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表