一 ,到底什么是hash呢?

作者:知乎用户
链接:https://www.zhihu.com/question/26762707/answer/40119521
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

hash(散列、杂凑)函数,是将任意长度的数据映射到有限长度的域上。直观解释起来,就是对一串数据m进行杂糅,输出另一段固定长度的数据h,作为这段数据的特征(指纹)。
也就是说,无论数据块m有多大,其输出值h为固定长度。到底是什么原理?将m分成固定长度(如128位),依次进行hash运算,然后用不同的方法迭代即可(如前一块的hash值与后一块的hash值进行异或)。如果不够128位怎么办?用0补全或者用1补全随意,算法中约定好就可以了。
原问题回答完毕。但是既然要说hash算法,不妨说的更透彻些。
=================分割线==========
由于用途的不同,hash在数据结构中的含义和密码学中的含义并不相同,所以在这两种不同的领域里,算法的设计侧重点也不同。

预备小知识:
抗碰撞能力:对于任意两个不同的数据块,其hash值相同的可能性极小;对于一个给定的数据块,找到和它hash值相同的数据块极为困难。
抗篡改能力:对于一个数据块,哪怕只改动其一个比特位,其hash值的改动也会非常大。
在用到hash进行管理的数据结构中,比如hashmap,hash值(key)存在的目的是加速键值对的查找,key的作用是为了将元素适当地放在各个桶里,对于抗碰撞的要求没有那么高。换句话说,hash出来的key,只要保证value大致均匀的放在不同的桶里就可以了。但整个算法的set性能,直接与hash值产生的速度有关,所以这时候的hash值的产生速度就尤为重要,以JDK中的String.hashCode()方法为例:

    public int hashCode() {int h = hash;//hash default value : 0 if (h == 0 && value.length > 0) {//value : char storagechar val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}

很简洁的一个乘加迭代运算,在不少的hash算法中,使用的是异或+加法进行迭代,速度和前者差不多。

在密码学中,hash算法的作用主要是用于消息摘要和签名,换句话说,它主要用于对整个消息的完整性进行校验。举个例子,我们登陆知乎的时候都需要输入密码,那么知乎如果明文保存这个密码,那么黑客就很容易窃取大家的密码来登陆,特别不安全。那么知乎就想到了一个方法,使用hash算法生成一个密码的签名,知乎后台只保存这个签名值。由于hash算法是不可逆的,那么黑客即便得到这个签名,也丝毫没有用处;而如果你在网站登陆界面上输入你的密码,那么知乎后台就会重新计算一下这个hash值,与网站中储存的原hash值进行比对,如果相同,证明你拥有这个账户的密码,那么就会允许你登陆。银行也是如此,银行是万万不敢保存用户密码的原文的,只会保存密码的hash值而而已。
在这些应用场景里,对于抗碰撞和抗篡改能力要求极高,对速度的要求在其次。一个设计良好的hash算法,其抗碰撞能力是很高的。以MD5为例,其输出长度为128位,设计预期碰撞概率为,这是一个极小极小的数字——而即便是在MD5被王小云教授破解之后,其碰撞概率上限也高达,也就是说,至少需要找次才能有1/2的概率来找到一个与目标文件相同的hash值。而对于两个相似的字符串,MD5加密结果如下:

MD5("version1") = "966634ebf2fc135707d6753692bf4b1e";
MD5("version2") = "2e0e95285f08a07dea17e7ee111b21c8";

可以看到仅仅一个比特位的改变,二者的MD5值就天差地别了。

二,什么是hash碰撞呢?怎么处理?

如果两个输入串的hash函数的值一样,则称这两个串是一个碰撞(Collision)。既然是把任意长度的字符串变成固定长度的字符串,所以必有一个输出串对应无穷多个输入串,碰撞是必然存在的。

一个优良的hash函数 f 应当满足以下三个条件:

(1)对于任意y,寻找x,使得f(x)=y,在计算上是不可行的。

(2)给定x1∈A,找x2∈B,,使得f(x1)=f(x2),在计算上是不可能的,这也就是弱无碰撞性。

(3)寻找x1,x2,使得f(x1)=f(x2),在计算上也是不可行的,这也就是强无碰撞性。

这样就称为安全保密的Hash函数,除了枚举外不可能有别的更快的方法。如第3条,根据生日定理,要想找到这样的x1,x2,理论上需要大约2^(n/2)的枚举次数。

因为前两条都能被破坏的hash函数太弱而被抛弃,几乎所有的hash函数的破解,都是指的破坏上面的第3条性质,即找到一个碰撞。在密码学上还有一个概念是理论破解,指的是提出一个算法,使得可以用低于理论值得枚举次数找到碰撞。

4、碰撞处理

  通常有两类方法处理碰撞:开放寻址(Open Addressing)法和链接(Chaining)法。前者是将所有结点均存放在散列表T[0..m-1]中;后者通常是把散列到同一槽中的所有元素放在一个链表中,而将此链表的头指针放在散列表T[0..m-1]中。

(1)开放寻址法

  所有的元素都在散列表中,每一个表项或包含动态集合的一个元素,或包含NIL。这种方法中散列表可能被填满,以致于不能插入任何新的元素。在开放寻址法中,当要插入一个元素时,可以连续地检查或探测散列表的各项,直到有一个空槽来放置待插入的关键字为止。有三种技术用于开放寻址法:线性探测、二次探测以及双重探测。

<1>线性探测

  给定一个普通的散列函数h':U —>{0,1,.....,m-1},线性探测方法采用的散列函数为:h(k,i) = (h'(k)+i)mod m,i=0,1,....,m-1  

探测时从i=0开始,首先探查T[h'(k)],然后依次探测T[h'(k)+1],…,直到T[h'(k)+m-1],此后又循环到T[0],T[1],…,直到探测到T[h'(k)-1]为止。探测过程终止于三种情况: 
  (1)若当前探测的单元为空,则表示查找失败(若是插入则将key写入其中); 
  (2)若当前探测的单元中含有key,则查找成功,但对于插入意味着失败; 
  (3)若探测到T[h'(k)-1]时仍未发现空单元也未找到key,则无论是查找还是插入均意味着失败(此时表满)。

线性探测方法较容易实现,但是存在一次群集问题,即连续被占用的槽的序列变的越来越长。采用例子进行说明线性探测过程,已知一组关键字为(26,36,41,38,44,15,68,12,6,51),用除余法构造散列函数,初始情况如下图所示:

散列过程如下图所示:

<2>二次探测

  二次探测法的探查序列是:h(k,i) =(h'(k)+i*i)%m ,0≤i≤m-1 。初次的探测位置为T[h'(k)],后序的探测位置在次基础上加一个偏移量,该偏移量以二次的方式依赖于i。该方法的缺陷是不易探查到整个散列空间。

<3>双重散列

  该方法是开放寻址的最好方法之一,因为其产生的排列具有随机选择的排列的许多特性。采用的散列函数为:h(k,i)=(h1(k)+ih2(k)) mod m。其中h1和h2为辅助散列函数。初始探测位置为T[h1(k)],后续的探测位置在此基础上加上偏移量h2(k)模m。

(2)链接法

  将所有关键字为同义词的结点链接在同一个链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。

  举例说明链接法的执行过程,设有一组关键字为(26,36,41,38,44,15,68,12,6,51),用除余法构造散列函数,初始情况如下图所示:

最终结果如下图所示:

三 ,为什么HashMap初始容量是2<<3?

如果两个元素不相同,但是hash函数的值相同,这两个元素就是一个碰撞

因为把任意长度的字符串变成固定长度的字符串,所以存在一个hash对应多个字符串的情况,所以碰撞必然存在

为了减少hash值的碰撞,需要实现一个尽量均匀分布的hash函数,在HashMap中通过利用key的hashcode值,来进行位运算
公式:index = e.hash & (newCap - 1)

举个例子:
1.计算"book"的hashcode
    十进制 : 3029737
    二进制 : 101110001110101110 1001

2.HashMap长度是默认的16,length - 1的结果
    十进制 : 15
    二进制 : 1111

3.把以上两个结果做与运算
    101110001110101110 1001 & 1111 = 1001
    1001的十进制 : 9,所以 index=9

hash算法最终得到的index结果,取决于hashcode值的最后几位

为了推断HashMap的默认长度为什么是16
现在,我们假设HashMap的长度是10,重复刚才的运算步骤:
hashcode : 101110001110101110 1001
length - 1 :                                     1001
index :                                            1001

再换一个hashcode 101110001110101110 1111 试试:
hashcode : 101110001110101110 1111
length - 1 :                                     1001
index :                                            1001

从结果可以看出,虽然hashcode变化了,但是运算的结果都是1001,也就是说,当HashMap长度为10的时候,有些index结果的出现几率
会更大而有些index结果永远不会出现(比如0111),这样就不符合hash均匀分布的原则

反观长度16或者其他2的幂,length - 1的值是所有二进制位全为1,这种情况下,index的结果等同于hashcode后几位的值
只要输入的hashcode本身分布均匀,hash算法的结果就是均匀的

所以,HashMap的默认长度为16,是为了降低hash碰撞的几率

到底什么是hash呢?hash碰撞?为什么HashMap的初始容量是16?相关推荐

  1. HashMap中hash(Object key)原理,为什么(hashcode >>> 16)。

    未经本人容许,禁止转载 大家都知道(jdk1.8)HashMap中计算数组下标是HashMap的核心算法.小编今天在看HashMap源码中看到了hash(Object key)方法百思不得其解.小编问 ...

  2. java hashmap hash算法,jdk1.8 中 HashMap 的 hash 算法和数组寻址

    开篇 本文基于 jdk1.8 讲述 HashMap 的 hash 算法,但是不会详细介绍其他相关内容(比如用法,底层数据结构).所以必须事先知晓下面几点: HashMap 的底层数据结构是数组,在数组 ...

  3. hash算法_到底什么是Hash?Hash算法的原理和实际应用讲解

    提到hash,相信大多数同学都不会陌生,之前很火现在也依旧很火的技术区块链背后的底层原理之一就是hash,下面就从hash算法的原理和实际应用等几个角度,对hash算法进行一个讲解. 1.什么是Has ...

  4. hash和hash碰撞以及解决方案

    hash: Hash,一般翻译做"散列",也有直接音译为"哈希"的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输 ...

  5. hash function/ hash table 背后的数学基础(二)

    hash function/ hash table 背后的数学基础 perfect hashing(完美哈希) 给定 nn 个键,构建一个静态表(static hash table,也即没有插入和删除 ...

  6. linux 命令缓存机制(命令:hash) | hash -r使用场景和作用

    文章目录 linux 命令缓存机制(命令:hash) | hash -r使用场景和作用 什么是hash 缓存表 hash命令 hash -r使用场景和作用 linux 命令缓存机制(命令:hash) ...

  7. Hash——字符串Hash

    Hash 在学习本节课之前,请大家思考这样一个问题:如果我们要在一个长度为 N N N的随机整数序列 A A A中统计每个数出现的次数,可以用什么方法? 不难想到,我们可以建立一个数组 ,然后将整数序 ...

  8. Hash(哈希)简述 —— Hash函数、Hash值、HashTable、HashMap

    总览 - Hash(哈希.散列) Hash是一种 散列函数或方法 的统称. · 该方法就是:把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值.-- (散列方法) · 这种转换是一种压 ...

  9. 【PHP】PHP实现Hash环/Hash一致性原理实现

    PHP实现Hash环/Hash一致性原理 Hash环可用于Redis机器集群.Mysql的分表操作,扩容时能对最大限度的减少损失 文章开头先引用大佬文章:什么是Hash环 class consisTe ...

最新文章

  1. Laravel安装因PHP版本不对的bug
  2. 一个震撼的交互型3D可视化R包 - 可直接转ggplot2图为3D
  3. zImage内核镜像解压过程详解
  4. vue set方法_Vue 数据响应式
  5. 算法:顺时针遍历矩阵 螺旋矩阵转换为顺时针列表spiral matrix
  6. elas算法源码赏析(一):PGM格式图片的读取和保存
  7. 计算机网络实验报告实验台,计算机控制实验台
  8. Modern CMake 简介
  9. Css3中添加-moz-, -webkit-, -o-,是什么意思呢?
  10. 电路设计_自恢复保险丝参数说明
  11. rm命令删除特定文件或目录或不删除特定文件或目录
  12. java中fido_一文读懂华为FIDO2指纹/3D 面容登录技术
  13. 物联网平台 yyds
  14. 【工大SCIR】首次探索中文词信息增强中文口语语言理解!
  15. 个人博客标签和文章的表结构设计
  16. 学习笔记 51单片机通用软件延时方法
  17. 爬虫新手入门(二):爬取一本言情小说的内容
  18. 第1阶段——关于u-boot目标文件start.o中.globl 和.balignl理解(3)
  19. async中的this指向问题
  20. Android 修改默认的背景壁纸(msm8909)

热门文章

  1. Dell iDRAC Express版本的共享网口设置
  2. linux图形界面键盘切换输入法,解决linux下不同图形环境的输入法切换问题
  3. Bright Data VPN和ClonBrowser:如何通过全球VPN网络保护你的在线隐私和安全?
  4. 请问在日本当一名程序员能挣多少钱啊
  5. 程序员这个职业赚钱吗?能赚多少钱?
  6. [模型]CNN、Text-CNN、PCNN学习笔记
  7. Oracle数据库:oracle 11g安装教程,已安装好的oracle各个文件夹的作用,oracle用户权限怎么样
  8. 更改WordPress语言设置(例如中文版和英文版转换)
  9. 【深度之眼吴恩达机器学习第四期】笔记(五)
  10. LiguerUi 和EasyUI的区别