hashcode

280 阅读4分钟

hashcode是通过hash算法生成的一个特征值。

hash算法其实算是一个思想:通过一个函数算出入参的特征值。作为一个特征值,那hashcode的作用其实就是标识入参,和入参有一个绑定关系,但是并不是说hashcode相同,入参就一定相同,取决于hash算法的实现。

假设我们要实现hashcode和入参唯一对应的hash函数,因为入参是一个无限的输入值域,如果要做到hashcode对不同的入参要唯一的话,hashcode的范围也是一个无限的输入值域,需要非常大的空间去保存,所以得仔细考虑场景是否适合。

一般的hash算法对于不同的入参都是有可能得到相同的hashcode,就像你把10个气球放到9个柜子里一样,肯定会有一个柜子存在两个气球,同样的,对于无限的入参集合而有限的hashcode存储空间,肯定也有入参不同但hashcode相同的情况,这叫做hash冲突。无论多强大的hash算法,只要你入参无限但输出集合S是非无限,那就肯定会存在hash冲突的问题,而hash冲突会有什么问题?降低查询效率。

举个具体的例子,比如说Java中的HashMap,当hash冲突发生后,会使用链地址法来处理冲突,当冲突越来越多,链就会越来越长,查询效率可能会退化成O(n),评价一个hash算法优劣的其中一点就是对于很多输入参数,输出结果是否均匀地在S上分布。

hashcode有很多不同的计算方式,比如说加法、乘法、除法、位运算、查表及混合。一般对不同场景会使用不同的hash算法。也有一些比较常用的hash算法,像md5、sha。Java的HashMap就使用了hashcode,它的一个put操作简化流程是:

  1. 由key算出hashcode
  2. 由hashcode再算出桶的位置
  3. 如果有冲突就解决冲突,没有冲突就直接赋值完成。

其实其他场景使用hashcode也差不多是这样的流程,因为通过hash算法得出的hashcode一般来说是会落在一个非常大的范围,比如java就会生成一个int数值,范围在-2147483648到+-2147483648,而我们的hash表范围一般比较小,像hashMap初始范围才16,所以得再将hashcode根据实际使用范围取余。

生成hashcode的hash算法有什么特征?

  1. 输入任意长度,输出固定长度: 哈希函数不用知道输入信息代表的是什么意思,也无所谓信息的长度有多长,只要输入hash函数出来的都是固定长度的比特值。比如非常有名的SHA256 哈希函数,输入任何值出来的都是256比特的0和1. 输入一本《三国演义》或者仅仅输入一个字母a,出来的都是256位比特长度的数据。

  2. 确定性: 当给哈希函数传入相同的输入值时,返回值都是一样。

  3. 抗碰撞性: 碰撞分强碰撞和弱碰撞。

  • 强碰撞:找出任意hashcode相同的两条消息
  • 弱碰撞:指定hashcode = A,找出一条hashcode = A的消息 所以抗碰撞性也得分两种。
  • 强抗碰撞性:找出任意hashcode相同的两条消息是非常困难的。
  • 弱抗碰撞性:指定hashcode = A,找出一条hashcode = A的消息是非常困难的。

具有完全抗碰撞性的hash算法可以用来做加密算法,如SM3、SHA,但是MD5及SHA-1算法已被证明不具备强抗碰撞性,所以我们在使用加密的hash算法的时候最好不用MD5及SHA-1算法。

  1. 隐藏性和单向性: 哈希函数的计算过程是单向不可逆的。x推出H(x),但是反推没有法子(单向性),也就是说,哈希值没有泄露输入的x的信息。也就是说x的信息被隐藏了起来,这也就就是隐藏性。

由于hash函数的隐藏性及单向性,可以很好地隐藏入参,比特币挖矿也是利用了这样的特性。在比特币中的挖矿的过程里实际上就是去找一个满足条件的入参(H(block header + nonce)≤target),由于hash函数的特点,矿工只能暴力破解,遍历所有可能性,这就变成了算力的比拼了。

  1. 不可预测: 不能简单地从入参推算出hashcode。