何谓哈希表
哈希函数
哈希表中哈希函数的实现步骤大致如下:
- 先生成key的哈希值(必须是整数)
- 再让key的哈希值跟数组的大小进行相关运算。 常见如下:
public int hash(Object key){
return hash_code(key) % table.length;
}
对key进行哈希,然后再对数组的长度进行模运算。 但是通常,为了提高效率,可以使用&位运算取代%运算。而前提是要将数组的长度设计为2的幂。
public int hash(Object key){
return hash_code(key) & (table.length-1);
}
如何生成key的哈希值
key的常见种类可能有字符串、整数、浮点数或者自定义对象。 不同种类的key的哈希值的生成方式是不一样的,但是他们的目标是一致的。
- 要尽量让每个key的哈希值是唯一的
- 尽量让key的所有信息都参与到运算中去。
而在Java中,HashMap的key必须实现hashCode、equals方法,也可以让key为null。Java里面的哈希值必须为int类型。
int整数
整数值就直接使用整数其本身作为哈希值。
float浮点数
浮点数 在 内存中也是二进制。那么就直接把其二进制的表示形式转化为整数。 在Java中可以使用 Float.floatToIntBits() 来将浮点数转化为对应的整数。然后再调用Integer.toBinaryString()即可将其转化为对应的二进制形式。
如: 对于浮点数10.6,可以有:
int num = Float.floatToIntBits(10.6f);
System.out.println("10.6f对应的整数是" + num);
String binary = Integer.toBinaryString(num);
System.out.println("10.6f对应的整数转化后的二进制是" + binary);
输出结果:
10.6f对应的整数是1093245338
10.6f对应的整数转化后的二进制是1000001001010011001100110011010
所以浮点数10.6f的哈希值就是其对应的整数是1093245338。
Long类型的哈希值
对于Long类型,Java的官方的算法如下: Long是8个字节,64位的。
public static int hashCode(long value) {
return (int)(value ^ (value >>> 32));
}
意思是先让value值无符号右移32位,然后再和value值本身异或。这样得出来的便是Long的哈希值。 其中的^和>>>的作用是什么?其作用在于让高32bit 和 低32bit 混合计算出32bit的哈希值,从而充分利用所有信息计算出哈希值。
^ 异或,相同为0,不同为1。 如1|1=0 , 1|0=1 , 0|0=0;
如图所示,最后我们得到的哈希值是橙色部分的二进制转化为的整数。
Double类型的哈希值
对于Double类型的哈希值算法如下: Double类型同Long类型,也是8字节64位的,因此其求哈希值的方式与Long大同小异。 但是Double多了一步doubleToLongBits,用于将Double的小数转化为相对应的Long类型。 这一步和float有相似,都要先从对应的小数转化为对应的整数或长整数。
public static int hashCode(double value) {
long bits = doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
字符串的哈希值
字符串的哈希值与前面的都有所不同。
我们可以通过类比来说明下。 比如,对于一个数字 8536,其实我们可以将其表示成: 8 × 10^3 + 5 × 10^2 + 3 × 10^1 + 6 × 10^0
那么同样的,对于一个字符串,我们也可以通过类似的方法来将其表示。 比如对于“Sitr”,可以表示成: S × n^3 + i × n^2 + t ×n^1 + r × n^0
这里的n表示字符的进制。 然后我们再把n提取公因数: n ×( n × (S × n + i ) + t) + r 也即: [(S × n + i) × n + t] × n + r
那么这里的n如何决定呢? 在JDK的字符串的HashCode里面,是将n视为31。 这是因为31是一个奇素数,进行哈希的时候更容易得到一个唯一值。而且在JDK里面,31 × i还可以进行优化,优化成 (i << 5) - i。
注1:此时i表示字符串的某个字符的对应的ANSII数。 注2:31 × i转化的推导 31 × i = (2^5 - 1) × i = i × 2^5-i = (i<<5) - i
见JDK中的字符串的HashCode:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}