Object内还有一个hashcode方法,留给子类实现的,但是本身Object的hashcode方法也是可以返回一串hash值的,但是是基于C++写的,本人表示看不懂~~。
贴几个博客留待研究吧,表示确实不清楚是怎么得到,需要去研究C++去了。暂时读不懂.
http://blog.youkuaiyun.com/luanlouis/article/details/41547649
http://blog.youkuaiyun.com/cor_twi/article/details/46554641
因此本篇其实是借由Object去研究一些常见类的hashcode实现。
1. 首先来看String的,
其实对于String来说的话,应该有一篇单独的文章去研究其内部的方法的,但是就先来看看吧。
value数组就是你String给的值,底层是放在一个数组内的 至于hash值,是一个int类型的变量。
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
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;
}
hashcode内最主要的方法 就是 h = 31 * h + val[i]。
//Test str
String str = "abcd";
//默认值
h = 0;
value.legth = 4;
val[a];val[b];val[c];val[d];
//进入循环
i=0;
h = 31 * 0 + a = a;
i=1;
h = 31 * (31 * 0 + a) + b = 31 * a + b
i=2;
h = 31 * (31 * (31 * 0 + a) + b) + c = 31 * 31 * a + 31 * b + c
i=3;
h = 31 * (31 * (31 * (31 * 0 + a) + b) + c) + d = 31 * 31 * 31 * a + 31 * 31 * b + 31 * c + d
可以发现:
h = 31^(n-1) * val[0] + 31^(n-2) * val[1] + 31^(n-3) * val[2] + ....+val[n-1]
其实上过学的人都能总结出这个公式,但是这个公式是什么意思,肯定是参考的某些算法实现的吧,不然依据呢?比如,为什么系数是31。我肯定是不知道为什么的了,百度看看吧,学习学习。
可以参考这个帖子: http://www.it165.net/pro/html/201410/24949.html
这个算法叫BKDRhash算法;顺便学习一下,看看这个算法做了什么。具体不想再去搬运工了。
重点看它分析为什么取偶数是不行的,选择31的原因我看后觉得因为它是素数,被舍弃的只是偶数部分,但是 文章里分析的 “1”那一部分不参与运算的,其次 31(11111)占5bits,很小。
另外,联系到hashmap上我们可以知道,其实得到了这个hash值,并不是直接去使用的,而是再去把它 和数组 做 取余操作,得到最后真正的地址。那么即使发生碰撞了,也不要紧,可以采用链表法解决冲突。
另外装载因子= 添加的记录数 / hash表的大小 ,默认选择 0.75 就是想不至于 因子太小而造成 资源浪费。
hash分布
2. Integer 的hashcode
public int hashCode() {
return value;
}
为什么只返回 value值就作为 hash值啊。
3. Long 的hashcode
public int hashCode() {
return (int)(value ^ (value >>> 32));
}
无符号右移32位 然后做 异或 操作。
所以SUM(ahijklmn)等于SUM(bhijklmn),这就是为什么” hijklmn”不变时,不管前面是什么字符串都会被舍弃,得到一样的字符串。这里用的是32=2^5,只要你用2^n,n不管为多少都不行,都会因为字符串的长度达到一定值而造成前面的被舍弃,造成一直碰撞。
c. 取奇数(大于1)
假如我们取9=2^3+1,9^2=81=80+1,9^3=729=728+1,… ,9^n=9^n-1+1,我们知道9的幂肯定是奇数,那么9^n-1肯定为偶数,由上面的推论可知字符串达到一定的长度时,偶数系数前面的字符是可以舍弃的,可是9^n=9^n-1+1,最后的1是永远不会被舍弃的,所以每个字符都会参与运算,取大于1的奇数可行。
但是在使用bkdrhash时你会发现里面大多源码使用的都是特殊的奇数2^n-1,那是因为在CPU的运算中移位和减法比较快。