0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

哈希表是什么?为什么需要使用哈希表

Wildesbeast 来源:网络整理 作者:佚名 2020-04-06 13:50 次阅读

我们在这篇文章将要学习最有用的数据结构之一—哈希表,哈希表的英文叫 Hash Table,也可以称为散列表或者Hash 表。

哈希表用的是数组支持按照下标随机访问数据的特性,所以哈希表其实就是数组的一种扩展,由数组演化而来。可以说,如果没有数组,就没有散列表。

哈希表存储的是由键(key)和值(value)组成的数据。例如,我们将每个人的性别作为数据进行存储,键为人名,值为对应的性别,其中 M 表示性别为男,F 表示性别为女。

为什么需要哈希表?

为了和哈希表进行对比,我们先将这些数据存储在数组中。

此处准备了6个箱子(即长度为6的数组)来存储数据,假设我们需要查询 Ally 的性别,由于不知道 Ally 的数据存储在哪个箱子里,所以只能从头开始查询,这个操作便叫作线性查找。一般来说,我们可以把键当成数据的标识符,把值当成数据的内容。

从 0 号箱子开始查找,发现 0 号箱子中存储的键是 Joe 而不是 Ally,因此接着查找 1 号箱子。

哦豁,1 号箱子中的也不是 Ally,没办法,只能接着往下找。

有点小糟糕,2 号、3 号箱子中的也都不是 Ally。

功夫不负有心人,当我们查找到 4 号箱子的时候,发现其中数据的键为 Ally,把键对应的值取出,我们就知道 Ally 的性别为女(F)。

通过上面的查找过程,我们发现数据量越多,线性查找耗费的时间就越长。由此可知:由于数据的查询较为耗时,所以此处并不适合使用数组来存储数据。

但使用哈希表便可以解决这个问题,首先准备好数组,这次我们用 5 个箱子的数组来存储数据。

尝试把 Joe 存进去,使用哈希函数(Hash)计算 Joe 的键,也就是字符串 Joe 的哈希值,比如得到的结果为4928。

将得到的哈希值除以数组的长度 5,求得其余数,这样的求余运算叫作mod运算,此处mod运算的结果为3。

因此,我们将 Joe 的数据存进数组的 3 号箱子中,重复前面的操作,将其他数据也存进数组中。

Sue 键的哈希值为 7291, mod 5 的结果为 1,将 Sue 的数据存进 1 号箱中。

Dan 键的哈希值为 1539, mod 5 的结果为 4,将 Dan 的数据存进 4 号箱中。

Nell 键的哈希值为 6276, mod 5 的结果为 1,本应将其存进数组的 1 号箱中,但此时 1 号箱中已经存储了 Sue 的数据,这种存储位置重复了的情况便叫作冲突。

遇到这种情况,可使用链表在已有数据的后面继续存储新的数据(链表法)。

Ally 键的哈希值为 9143, mod 5 的结果为 3,本应将其存储在数组的 3 号箱中,但 3 号箱中已经有了 Joe 的数据,所以使用链表,在其后面存储 Ally 的数据。

Bob 键的哈希值为 5278, mod 5 的结果为 3,本应将其存储在数组的 3 号箱中,但 3 号箱中已经有了 Joe 和 Ally 的数据,所以使用链表,在 Ally 的后面继续存储 Bob 的数据。

像这样存储完所有数据,哈希表也就制作完成了。

接下来讲解数据的查询方法,假设我们要查询 Dan 的性别。

为了知道 Dan 存储在哪个箱子里,首先需要算出 Dan 键的哈希值,然后对其进行 mod 运算,最后得到的结果为 4,于是我们知道了它存储在 4 号箱中。

查看 4 号箱可知,其中的数据的键与 Dan 一致,于是取出对应的值,由此我们便知道了 Dan 的性别为男(M)。

那么,想要查询 Ally 的性别时该怎么做呢?为了找到它的存储位置,先要算出 Ally 键的哈希值,再对其进行 mod 运算,最终得到的结果为 3。

然而 3 号箱中数据的键是 Joe 而不是 Ally,此时便需要对 Joe 所在的链表进行线性查找。

于是我们找到了键为 Ally 的数据,取出其对应的值,便知道了 Ally 的性别为女(F)。

哈希冲突

在哈希表中,我们可以利用哈希函数快速访问到数组中的目标数据。如果发生哈希冲突,就使用链表进行存储,这样一来,不管数据量为多少,我们都能够灵活应对。

如果数组的空间太小,使用哈希表的时候就容易发生冲突,线性查找的使用频率也会更高;反过来,如果数组的空间太大,就会出现很多空箱子,造成内存的浪费。因此,给数组设定合适的空间非常重要。

在存储数据的过程中,如果发生冲突,可以利用链表在已有数据的后面插入新数据来解决冲突,这种方法被称为链表法,也被称为链地址法。

其中在 Java 集合类的 HashMap 中解决冲突的方法就是采用的链表法,建议阅读 HashMap 源码。

除了链地址法以外,还有几种解决冲突的方法。其中,应用较为广泛的是开放地址法,或称为开放寻址法。这种方法是指当冲突发生时,立刻计算出一个候补地址(数组上的位置)并将数据存进去。如果仍然有冲突,便继续计算下一个候补地址,直到有空地址为止,可以通过多次使用哈希函数或线性探测法等方法计算候补地址。

在 Java 中,ThreadLocal所使用的就是开放地址法。

哈希函数设计的好坏决定了哈希冲突的概率,也就决定哈希表的性能。

总结

这篇文章主要讲了一些比较基础的哈希表知识,包括哈希表的由来、哈希冲突的解决方法。

哈希表也叫散列表,来源于数组,它借助哈希函数对数组这种数据结构进行扩展,利用的是数组支持按照下标随机访问元素的特性,是存储 Key-Value 映射的集合。

哈希表两个核心问题是哈希函数设计和哈希冲突解决。对于某一个 Key,哈希表可以在接近 O(1) 的时间内进行读写操作。哈希表通过哈希函数实现 Key 和数组下标的转换,通过开放寻址法和链表法来解决哈希冲突。哈希函数设计的好坏决定了哈希冲突的概率,也就决定哈希表的性能。

有兴趣的可以在 JDK 中阅读 HashMap 的源码,在 JDK 8 和之前的版本的实现还有许多不多,比如在 JDK 8 中,引入红黑树,当链表长度太长(默认超过 8)时,链表就转换为红黑树,就可以利用红黑树快速增删改查的特点,提高 HashMap 的性能。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据存储
    +关注

    关注

    5

    文章

    957

    浏览量

    50827
  • 函数
    +关注

    关注

    3

    文章

    4276

    浏览量

    62316
  • 数据结构
    +关注

    关注

    3

    文章

    569

    浏览量

    40072
收藏 人收藏

    评论

    相关推荐

    什么是默克尔树(Merkle Tree)?如何计算默克尔根?

    01 默克尔树的概念 默克尔树(Merkle Tree)是一种特殊的二叉树,它的每个节点都存储了一个数据块的哈希值。哈希值是一种可以将任意长度的数据转换为固定长度的字符串的算法,它具有唯一性和不可
    的头像 发表于 09-30 18:22 462次阅读
    什么是默克尔树(Merkle Tree)?如何计算默克尔根?

    开源物联网技术--哈希算法MD5加密功能技术分享

    MD5(Message-Digest Algorithm 5)是一种常用的哈希函数,通常用于数据加密和安全校验等场合。MD5 算法可以将任意长度的消息输入计算出一个固定长度的摘要,其生成的摘要具有
    的头像 发表于 09-21 09:57 773次阅读
    开源物联网技术--<b class='flag-5'>哈希</b>算法MD5加密功能技术分享

    ESP8266如何避免固件损坏?

    我们需要在固件下载中采用强大的固件升级方法,我们知道在固件下载中有一个示例。 例子/at_upgrade.c 对于该示例,我们有一个问题: 如何避免损坏的bin文件? 因为如果恶意用户将损坏
    发表于 07-19 06:00

    吉时利2400源电压怎么调

    功能和高精度、高稳定性等特点。 吉时利2400源(Keithley 2400 SourceMeter)是一款高性能的源测量单元(SMU),它可以作为电压源、电流源、电压和电流使用。在调整吉时利2400源
    的头像 发表于 05-23 17:06 616次阅读
    吉时利2400源<b class='flag-5'>表</b>电压怎么调

    PD电流

    PD电流
    蛋黄酥
    发布于 :2024年02月24日 20:20:13

    CAN总线系统需要使用中继器进行信号增强和延长?

    CAN总线系统需要使用中继器进行信号增强和延长? CAN总线系统是是一种用于微控制器和设备之间通信的串行总线系统。作为一种主要用于汽车和工业领域的通信协议,CAN总线系统需要使用中继器进行信号增强
    的头像 发表于 01-31 13:46 805次阅读

    真有效值万用和平均值万用的区别?

    最近在看福禄克的官网看到几款万用,15B+和17B+,因为自己也是在用这两款万用,就看了看这两款万用的参数,之前一直以为这两款是真有效值万用,谁知道是均值万用
    发表于 01-09 17:27

    redis hash底层实现原理

    数据结构是如何实现的呢?本文将详细介绍Redis哈希底层的实现原理。 在Redis中,每个哈希都是由一个类似于字典(Dictionary)的结构实现的,其中使用链地址法解决哈希冲突。整个哈希
    的头像 发表于 12-04 16:27 541次阅读

    为什么需要使用“0欧姆电阻”?

    为什么需要使用“0欧姆电阻”?
    的头像 发表于 11-30 15:52 575次阅读
    为什么<b class='flag-5'>需要使</b>用“0欧姆电阻”?

    请问SigmaStudio RMS Compressor34个点的参数值如何计算?

    我现在有一个adau1442项目,用ARM控制DSP的参数,需要使用RMS COMPRESSOR,现在的问题是调整参数时,中的34个点的参数值如何计算? ]
    发表于 11-30 07:18

    为什么需要使用多路输出的电源适配器?

    为什么需要使用多路输出的电源适配器? 多路输出的电源适配器是一种可以同时供电多个设备的电源适配器。在很多情况下,使用多路输出的电源适配器是非常必要的。 首先,使用多路输出的电源适配器可以提高工作效率
    的头像 发表于 11-23 15:34 904次阅读

    为什么需要使用交流稳定化电源呢?交流稳定化电源有哪些作用呢?

    为什么需要使用交流稳定化电源呢?交流稳定化电源有哪些作用呢? 交流稳定化电源是一种用于稳定交变电压的电源设备,它在电力系统建设、电子设备制造和实验室应用中具有重要的作用。本文将详尽、详实、细致地介绍
    的头像 发表于 11-17 14:49 556次阅读

    redis的数据结构一般分为哪几种?

    缓存、计数器、分布式锁等。字符串类型支持很多操作,如设置、获取、删除、追加等。 哈希(Hashes): 哈希是 Redis 提供的一个键值对的数据结构,它类似于一个字典,可以存储多
    的头像 发表于 11-16 11:19 404次阅读