一、int类型、float类型
int类型很简单,直接返回该int即可
public static int hashCode(int value) {
return value;
}
float类型把它转为字节,再将字节转为int类型即可
public static int hashCode(float value) {
return floatToIntBits(value);
}
二、long类型、double类型
long类型和double类型由于都是8个字节(64位),无法直接转换位4个字节的int类型,并且在hash算法中,尽量要做到每一位都参与运算,于是我们先将它无符号右移32位,即将它的高32位复制到低32位,并且在高32位补0,然后再与它本身做异或运算,再把结果强转为int类型即可
public static int hashCode(long value) {
return (int)(value ^ (value >>> 32));
}
double也是同理,先将double转为字节,然后再进行上述操作
public static int hashCode(double value) {
long bits = doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
三、String类型
首先我们先想一个问题,7346这个数字如何表示呢?很容易想到:
7346 = 7 ∗ 1 0 3 + 3 ∗ 1 0 2 + 4 ∗ 1 0 1 + 6 ∗ 1 0 0 7346 = 7*10^3 + 3*10^2 + 4*10^1 + 6*10^0 7346=7∗103+3∗102+4∗101+6∗100
字符串的hash算法也是类似的,例如我们要对"hello"这个字符串做hash,那它的计算过程是:
h a s h ( “ h e l l o ” ) = ‘ h ’ ∗ n 4 + ‘ e ’ ∗ n 3 + ‘ l ’ ∗ n 2 + ‘ l ’ ∗ n 1 + ‘ o ’ ∗ n 0 hash(“hello”) = ‘h’ * n^4 + ‘e’*n^3 + ‘l’*n^2 + ‘l’ *n^1 + ‘o’ * n^0 hash(“hello”)=‘h’∗n4+‘e’∗n3+‘l’∗n2+‘l’∗n1+‘o’∗n0
在这个运算中,其实有一些的运算是多余的,例如计算 n 4 n^4 n4,我们完全可以使用后面计算的 n 3 ∗ n n^3 * n n3∗n来得到, n 3 n^3 n3也是同理,所以上面那个计算过程就等价于:
( ( ( ( ‘ h ’ ∗ n + ‘ e ’ ) ∗ n ) + ‘ l ’ ) ∗ n + ‘ l ’ ) ∗ n + ‘ o ’ ((((‘h’ * n + ‘e’)*n)+‘l’)*n+‘l’)*n+‘o’ ((((‘h’∗n+‘e’)∗n)+‘l’)∗n+‘l’)∗n+‘o’
解决了计算过程的问题,那接下来问题就来了,n取多少合适呢?
在JDK内部,它的n取值为31,为什么是31呢?
31是一个很神奇的数,它是一个奇素数,并且任何一个数i * 31 就等于 (i << 5) - i,要知道,位运算要比乘法运算效率高的多,JVM也会针对这个计算过程自动做优化
具体可以看一下这个博客,里面分析的很到位
看一下JDK1.8中String类的实现,实际上就是计算了一次上述的公式:
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;
}
四、自定义对象
自定义对象的hash方法与String类似,可以使用该对象的所有属性依次计算其hash值后,再套用上面说的公式返回最终的hash值
可以查看一下JDK的实现
java.util.Objects.hash方法:
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
java.util.Arrays.hashCode方法:
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}