哈希表你真的了解吗 如何处理哈希碰撞

大家好,我是三叔,很高兴这期又和大家见面了,一个奋斗在互联网的打工人。

什么是hash表

哈希表是一种常见的数据结构,也被称为散列表(Hash Table)或哈希映射(Hash Map)。它通过使用哈希函数(Hash Function)将键(Key)映射到存储桶(Bucket)中,以实现高效的数据存储和检索。
hash表常见的数据结构有:数组、Set、Map。

  1. 数组:哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。
    数组是一种最基本的数据结构,它可以连续地存储多个相同类型的元素。数组的访问速度非常快,因为可以通过索引直接访问元素。然而,数组的大小是固定的,一旦创建后无法动态改变。此外,数组不提供内置的键值对关系。

适用场景:当需要使用一个简单的、固定大小的数据结构来存储一组元素时,可以选择数组。例如,存储一组整数、浮点数或自定义对象等。
在这里插入图片描述

例如要查询一个班级学生是否在这所学校里,要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。

只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。将学生姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数。

哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
在这里插入图片描述
如果hashCode得到的数值大于 哈希表的大小了,也就是大于tableSize了,怎么办呢?

此时为了保证映射出来的索引数值都落在哈希表上,我们会在再次对数值做一个取模的操作,就要我们就保证了学生姓名一定可以映射到哈希表上了。

  1. Set
    Set是一种集合类型的数据结构,它存储一组唯一的元素,不允许重复。Set的实现类包括HashSet、LinkedHashSet和TreeSet。HashSet使用哈希表实现,具有较快的插入和查找速度;LinkedHashSet在HashSet的基础上维护了元素的插入顺序;TreeSet使用红黑树实现,可以对元素进行排序。

适用场景:当需要存储一组唯一元素,并且不关心元素的顺序时,可以选择Set。常见的应用包括去重 等。

  1. Map
    Map是一种键值对的数据结构,每个元素由一个键和一个对应的值组成。Map的实现类包括HashMap、LinkedHashMap和TreeMap。HashMap使用哈希表实现,提供了快速的键值对存储和查找;LinkedHashMap在HashMap的基础上维护了键值对的插入顺序;TreeMap使用红黑树实现,可以对键进行排序。

适用场景:当需要按照键值对关系存储和检索数据时,可以选择Map。常见的应用包括缓存、索引、配置信息等。

那谁的效率更高?

如果数据量不大的情况下,使用数组效率更高,Set和Map进行映射需要时间;如果是数据量比较大,使用Set和Map效率更高,因为底层是树,树的查找效率更高。

hash碰撞

既然是hash函数进行计算,数据量足够多的时候或者hash函数分布不够均匀,总是会有不同学生的hash值一样,那么就会产生hash碰撞。

如图所示,小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞。哈希碰撞指的是不同的键通过哈希函数计算后得到相同的哈希值,导致它们被映射到同一个存储桶中。哈希碰撞是不可避免的,因为哈希函数的输出范围有限,而键的数量可能是无限的。
在这里插入图片描述

hash碰撞常见处理方法
  1. 拉链法:
    将发生冲突的数据通过链表的形式进行存储:
    在这里插入图片描述
  2. 放寻址法
    开放寻址法是另一种处理哈希碰撞的方法。当发生碰撞时,它尝试将碰撞的键值对直接存储在其他可用的存储桶中,而不是创建链表。一种常见的开放寻址法是线性探测法,它在发生碰撞时顺序地检查下一个存储桶,直到找到一个空的存储桶。
  3. 再哈希法(多次hash)
    再哈希法是一种解决哈希碰撞的策略,它通过应用另一个哈希函数来处理碰撞。当发生碰撞时,使用第二个哈希函数重新计算键的哈希值,并将键值对放置在新的存储桶中。如果新的存储桶也发生碰撞,则继续使用再哈希法,直到找到一个空的存储桶。再哈希法可以减少碰撞的概率,但需要选择和设计合适的哈希函数。
  4. 负载因子和动态调整
    负载因子是指哈希表中存储的键值对数量与存储桶数量的比值。为了保持哈希表的性能,通常需要控制负载因子的大小。当负载因子超过某个阈值时,可以动态调整哈希表的大小,即增加存储桶的数量。调整哈希表大小时,需要重新计算键的哈希值,并重新分配键值对到新的存储桶中。

在Java中,常见的哈希表实现(如HashMap、LinkedHashMap)已经内置了处理哈希碰撞的策略,采用了链表法和开放寻址法的组合,以及负载因子和动态调整的机制。它们通过优化的哈希函数和合理的碰撞处理策略,提供了高效的键值对存储和检索功能。

最后

哈希表是一种重要的数据结构,通过哈希函数将键映射到存储桶中。哈希碰撞是不可避免的,但我们可以通过链表法、开放寻址法、再哈希法以及负载因子的动态调整等策略来处理碰撞,以提高哈希表的性能和效率。在实际应用中,我们需要根据具体场景和需求选择合适的哈希表实现和碰撞处理策略。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在Java中,哈希表可能会经常出现哈希碰撞哈希表是一种根据键(Key)来访问值(Value)的数据结构,通过哈希函数将键映射到哈希表的索引位置上。由于哈希函数的映射结果可能不唯一,不同的键可能会被映射到同一个索引位置上,从而导致哈希碰撞哈希碰撞会影响哈希表的性能,因此需要采取一些策略来减少哈希碰撞的发生,例如使用更好的哈希函数、调整哈希表的容量等。 ### 回答2: 在Java中,哈希表HashMap)在处理大量数据时可能会出现哈希碰撞哈希碰撞是指不同的键(key)计算出的哈希值(hash value)相同的情况。由于哈希表使用哈希函数将键映射到数组索引,不同的键可能映射到相同的数组位置,这就导致了哈希碰撞的发生。 哈希碰撞的发生是无法避免的,因为哈希值的范围是无限的,而存储空间是有限的。无论是哪种哈希函数,都无法将无限的哈希值映射到有限的数组索引上,因此在开发过程中,我们要考虑如何处理和解决哈希碰撞。 在Java中,HashMap通过在发生哈希碰撞时使用链表(JDK8之前)或红黑树(JDK8及以后)来解决。当多个键映射到同一个数组索引上时,它们会组成一个链表或红黑树,使得查找、插入和删除键的操作仍然能够高效进行。 然而,当哈希表中的数据量变得非常大,链表或红黑树的长度过长时,会降低哈希表的性能。为了尽量避免哈希碰撞,我们可以尝试选用更好的哈希函数、调整哈希表的负载因子(load factor)或使用其他类型的哈希表实现,如ConcurrentHashMap。 综上所述,在Java中,哈希表HashMap)会经常出现哈希碰撞。然而,Java提供了相应的解决方案,以提高哈希表的性能并降低哈希碰撞的影响。 ### 回答3: 在Java中,哈希表(HashTable)实现了基于哈希函数的键值对存储和查找机制。哈希碰撞是指不同的键值对通过哈希函数计算后得到相同的哈希值,从而导致它们被映射到哈希表中相同的位置。 在Java哈希表实现中,哈希碰撞是可能发生的。这是因为哈希函数的计算过程无法避免某些键值对具有相同的哈希值。当不同的键值对具有相同的哈希值时,它们将被存储在哈希表的同一位置上。这会导致在查找键值对时需要额外的操作来解决冲突,例如链表法或开放寻址法。 然而,哈希碰撞的概率通常较低,且可以通过几种方法来减少碰撞的发生。首先,选择一个好的哈希函数可以降低碰撞的概率。好的哈希函数应该能够将键值对均匀地分布在哈希表中,尽量避免相同哈希值的出现。其次,哈希表的大小也会影响碰撞的概率,散列桶的数量越多,碰撞的可能性就越小。 在Java中,除了传统的哈希表实现HashTable,还有更常用的HashMap实现,它采用了更高效的哈希算法(散列函数)和碰撞解决方法。HashMap使用链表法和红黑树结构来处理碰撞,以提高性能和减少碰撞的发生。 总的来说,在Java哈希表会经常出现哈希碰撞,但可以通过选择好的哈希函数和适当的哈希表大小来减少碰撞的概率,同时Java中提供的高效哈希实现也能有效处理碰撞问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值