快速了解哈希表之包教包会(bushi

今天带到大家来简单了解一下哈希表

 一,前言——举个栗子

        大家可以想象一个这样的场景,假设我们开了一家超市,里面有许许多多的商品,此时一位顾客走到收银台说:“红烧牛肉面怎么卖?”,此时的我们有两种选择:

        1.根据商品清单,找到红烧牛肉面,再根据红烧牛肉面的商品信息找出对应的价格

        2.大喊一声:“红烧牛肉面5块!”

        很显然,我们都希望是后者,因为这样的速度似乎更快一些。

        从软件开发的角度看,在前者的做法中,商品清单相当于一个数组,其中存储着所有商品的名字和价格,现在我想知道红烧牛肉面多少钱一桶那么我们就需要搜索整个数组,看看那一项的商品名称是它,然后就可以得到它的价格了:

      商品名称     价格
       小汉堡      20
       小面包      30
       老干妈      10
       臭豆腐       5
    红烧牛肉面       5
        ……    ……

        这种做法的问题在于,每次搜索都要花很多时间,有可能你找到价格的时候顾客已经走掉了……

         这时候我们希望的是能够根据商品名称可以立刻得到它的价格,这并不难,我们只需要借助一个神奇的函数就可以实现了,它就是:哈希函数

二,哈希函数

        先不说这个函数的代码,我们先来讲讲它的功能:

        当我们将商品名称作为一个参数来调用它时,函数会返回一个整数,返回的整数和参数之间我们并不能看出有什么规律,当然整数也不是随便返回的,它首先必须满足两个条件:

        1.用不同的参数作为参数,必须返回不同的整数

         比如说葡萄做参数返回的是22,用橙子做参数返回的就是33,绝不可能是22

        2.同样的商品名称,必须返回同样的整数

         比如说这次葡萄做参数返回的是22,那么下次用葡萄做参数返回的还是33,绝不可能是33

        那么有了这样一个函数,剩下的事情就好办了。

        我们可以定义一个数组,这个数组用来存放商品的信息,当我们输入西瓜这个参数给函数的时候,那么就会返回一个整数,假设整数为66,那么我们就将数组索引值为66的位置存入西瓜的价格,又比如输入参数老干妈,函数就会返回213,同理我们就把老干妈的价格存到索引值为213的位置,如图:

…………………
662块钱一斤
…………………
21310块钱一瓶

        此时,有一个靓仔来买西瓜:

 

        老板就会立刻回答:

         停!!!为什么老板能立马得出西瓜的价格呢?因为他是这么处理的:

        先用西瓜作为参数调用函数,得到的返回值为66,然后他直接读出数组中索引值为66的对应元素,整个过程就在一瞬间就完成了,很快啊!

        我们可以看到哈希函数的效率还是杠杠的,那么哈希函数中的“哈希”是什么呢,可能一开始大家会觉得这是计算机界某个大佬的名字,但事实上这个哈希是由英语中的Hash音译过来的,Hash有把东西弄糟弄乱的意思,所以哈希函数也叫散列函数杂凑函数

        它的功能是:输入数据,生成整数。

        特点是:

                1.不同参数返回不同的整数

                2.相同参数返回相同的整数

        借助于哈希函数,就可以实现数据到存储位置的一对一映射,从而把数据(西瓜)的属性(价格)存放在一张表(数组)里,这种表就称为哈希表,也称为散列表。(也就是前面老板所用的数组,就是一个哈希表)

        接下来我们来聊聊哈希表的内部实现

三,哈希表的内部实现

        可能大家听着这个哈希函数听着非常地高大上,实现起来是不是需要什么非常高深的技术,但实际上也不尽然,俗话说得好:技术是为现实需要服务的。在某些特定的场合我们甚至可以自己设计算法来实现这个函数。

        有一个很容易理解的算法叫作“平方取中法”,简单来说就是对函数所传入的字符参数 key ,取它的unicode序号,这样就转成了数字,然后再做一个平方,再取平方结果中的三位数字作为返回值即可,例如输入'瓜'会返回967,再次输入'瓜'还是会返回967,而输入'梨'则会返回811,显然满足了哈希函数的两个特点,这就是哈希函数的简单实现。(此处的方法只是举例说明哈希表,真实的方法会更复杂一些)

        那么在Java当中的哈希表又是使用的什么算法呢,为了方便大家理解,这里我把源码的操作流程写出来让大家看一下:        

新增过程:
                     1.计算新增元素的哈希值

                     2.(假设数组已经创建出来),通过  hash%数组长度获得索引值
                     3.1如果索引值对应位置为null:则直接新增
                          3.2如果该位置不为null:
                                  判断该元素是否重复:
                              3.2.1如果不重复,则新增到该索引值位置链表的最后面
                              3.2.2如果重复:则不新增

        无序:因为哈希值的计算有各种可能,通过哈希值进行确定元素添加的位置,因此元素存储的无序性就不奇怪了

        特殊的:String覆盖重写了HashCode方法,只要内容相同,哈希值一定相同。

        那么此时就有一个问题:这种算法一定可以保证不重复吗,回答是:不能保证,而且事实上,任何算法在理论上都不能保证,这种传入不同的参数时结果却相同的情况学名叫作:冲突(Collision),冲突现象可能会出现,不过不要紧,我们还是有补救的方法的,不过代价就是牺牲一部分的性能和存储空间,至于如何设计降低冲突的算法,那是数学和计算机精英的事情,大家只需要使用已有的成熟算法即可,例如:SHA-256MD5……

四,哈希表扩容

        在JDK1.8之前:哈希表底层实现是数组+链表,在JDK1.8之后:底层实现是数组+链表或红黑树(或是指在未达到一定条件时它的底层运转依旧是数组+链表,下面对于哈希表底层在满足了什么情况会变成数组+红黑树做了几点描述)

1.当同一索引值下元素个数>8,并且数组长度<64

        (在哈希表中,通过链表来存储数据,通过数组记录索引值,设置每个索引之下不得多于8个元素,否则就要进行扩容或者转换为红黑树)
 

2.数组的索引值占有率75%

 扩容后的新容量 = 旧容量<<1 (也就是扩容后的新容量是旧容量的二倍)

        那么以上就是对哈希表的一个基本的介绍,希望大家可以看懂,有什么问题也可以发在评论区当中,我有时间就会回复!

  • 11
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值