如何完美解答面试问题——hashCode()如何设计

大家好,我是孤焰。今天要谈一谈在面试过程中几乎被每个面试官都会提到的一个问题——hashCode()如何设计?由于我也是刚刚学习编程的小白,所以此篇博文将参考了多篇博文,最后总结而成。

哈希函数的设计

1.什么是哈希函数?

 哈希函数(Hash Function),也称为散列函数,给定一个输入x,它会算出相应的输出H(x)

 这个H(x)相当于哈希表中的“键“,是哈希表中数组的索引值。

2.哈希函数的普遍设计方法

 “键”通过哈希函数得到的“索引”分布的越均匀越好,这样发生的哈希冲突的几率越低。

2.1 整型的哈希函数设计

 小范围正整数x:可以直接使用x当做索引值。

 小范围负整数x:若-100≤x≤100 ==> 则0≤x≤200 因为数组中没有小于0的索引值,所以可以将x的值偏移为正数后当做索引值。

 大正数x: 若x=123456789123456789,则可以对x取模(%)。

x%1000 等同于取后三位

 通常取得模越大,产生哈希冲突的概率就越小,但是如果数据是一组有规律的数字,如果模的数字选取不当,那么可能会产生较大的哈希冲突。例如:

下面图片的格式:整数1 % 整数2 -> 余数

在这里插入图片描述
一个简单的解决方法:模(%)一个素数,至于为什么模一个素数可以减少哈希冲突,属于数论的知识,这里不多赘述。

2.2 浮点型的哈希函数设计

 float类型(32位):可以将32位0 1序列转换为整型,然后同上述方法模一个素数。

在这里插入图片描述

 double类型(64位):可以将64位0 1序列转换为整型,然后同上述方法模一个素数,double的内存模型类似于float,这里不再展示。

2.3 字符串的哈希函数设计

 hash(166) = 1*10^2 + 6*10^1 + 6*10^0
 hash(166)可以被分解为1乘以10的平方 加上6乘以10的一次方 加上6乘以10的零次方(小范围正整数可以直接使用,无需取模)。


 hash(“code”) = (c * 26^3 + o * 26^2 + d * 26^1 + e * 26^0) % M
 同理,hash(code)可以被当做26进制(a~z共26个小写字母,如果算上大写字母或特殊符号,则可以换成其他进制)的算数求和

 根据以上观点可以抽象出以下公式:
hash(“code”) = (c * B^3 + o * B^2 + d * B^1 + e * B^0) % M(B代表进制)


 在计算机程序中还可以对以上公式进行优化,优化过程如下:

 首先进行如下优化:
 hash(“code”) = ((((c * B) + o) * B + d) * B + e) % M

 然后优化为如下公式:
hash(“code”) = ((((c % M) * B + o) % M * B + d) % M * B + e) % M

 此公式可以在程序中可以如下编写:

//hashCode()的普遍设计方法
int hash = 0;
for (int i = 0 ; i < s.length() ; ++i) {
    
    
	hash = (hash * B + s.charAt(i)) % M;
}

2.4 复合类型的哈希函数设计

 复合类型Date:year,month,day

 利用上述公式,不难写出:
 hash(Date) = (((date.year % M) * B + date.month) % M * B + date.day) % M

3.哈希函数设计的通用公式

 综上所述,哈希函数设计的通用公式为:

hash("code") = ((((c % M) * B + o) % M * B + d) % M * B + e) % M

 但是,将其他数据类型转化为整型再计算hash值,并不是唯一的方法,有许多复杂的数据类型需要具体问题具体分析

4.原则

  • 一致性:如果a==b,则hash(a) == hash(b),但是!!如果hash(a) == hash(b),则a不一定等于b
  • 高效性:计算高效简便
  • 均匀性:哈希值均匀分布

猜你喜欢

转载自blog.csdn.net/Handsome_Le_le/article/details/108698615
今日推荐