HashCode()为啥系数是素数


最近同事技术分享了下一致性hash,顺道打开JDK源码重新看了下hashcode(),发现有一些东西还是挺有意思。

源码

public static void main(String[] args) { 
         String s="abc"; 
        System.out.println(s.hashCode()); 
        //输出96354
} 

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()核心的点有几个:
  • value :char[] value= {'a', 'b', 'c'};
  • value[i]: 对应的ASCII值
  • 计算公式:h = 31 * h + val[i]
  • 计算结果
    ASCII的值为: a-->97 ; b->98 ; c->99
    hash值:31x( 31 x 97+98)+99= 31^2 * 97 + 31 ^ 1 * 98 + 31 ^ 0 * 99=96345

公式进一步表示为:s[0] * 31 ^ (n-1) + s[1] * 31 ^ (n-2) + ... + s[n-1]

  • n :字符串的长度
  • 31: 系数

所以现在的问题就变成了系数为什么是31?

原因

最根本的原因他是一个素数,他不能被任何整数整除,除了1以外。

为什么一定是要素数?

Hash的目的是要讲value以一个规则分配到一个存储结构里(比如说一个个桶吧),并且能够再快速的查出来。

那怎么确定这个value放在哪个桶里面?

一个原始的实现是:

 for ( i = 0; i < keys.size(); i++) {  
      if (isEqual(keys[i], "test")) 
      return i; 
}

会有两个问题:

  1. 这种查询性能不高,只有O(n)。
  2. key的value是String判断equal还好,但是如果是其他复杂的Type就复杂了。

解决方法:

把这些形式不一的key的值,统一到一个维度value里面,最好是全局唯一的
这样我们根据这个唯一键查找起来就能跟其他的分开了。

所以我们自然而然就想到的素数。

  1. 他自己本身唯一。
  2. 跟其他数相乘的结果唯一的可能性也很大。

为什么一定是31?

如果假定这个素数是3,我们的桶正好有3个,key的值[1,2,3],这样能够一一对应。

那如果key超出了3,比如是4,这时候还要放进去,但是不能重新增加桶的数量,只能叠加了,常用取模形式,4%3=1桶。

所以量越大,桶的数越小,碰撞的概率就大,通过hash获取的链越长,遍历的会越多,查询效率越低。

不过一个是用31取模,hashcode的31是乘积,不知道碰撞的结果是不是一致的。

据说经过多个素数的测试,得到系数是31的碰撞概率是最小的,不过查了很多资料,都没有这样的数据说明,坑爹。

你可能感兴趣的:(HashCode()为啥系数是素数)