hashcode及为什么选31

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HNUST_LIZEMING/article/details/88709589

Object内还有一个hashcode方法,留给子类实现的,但是本身Object的hashcode方法也是可以返回一串hash值的,但是是基于C++写的,本人表示看不懂~~。

贴几个博客留待研究吧,表示确实不清楚是怎么得到,需要去研究C++去了。暂时读不懂.

http://blog.csdn.net/luanlouis/article/details/41547649

http://blog.csdn.net/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

扫描二维码关注公众号,回复: 5627697 查看本文章

这个算法叫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的运算中移位和减法比较快。

猜你喜欢

转载自blog.csdn.net/HNUST_LIZEMING/article/details/88709589