为什么在定义hashcode时要使用31这个数呢?

散列计算就是计算元素应该放在数组的哪个元素里。准确的说是放到哪个链表里面。按照Java的规则,如果你要想将一个对象放入HashMap中,你的对象的类必须提供hashcode方法,返回一个整数值。比如String类就有如下方法:

[java] view
plain
copyprint?

  1. public int hashCode() {  
  2.         int h = hash;  
  3.         int len = count;  
  4.         if (h == 0 && len > 0) {  
  5.             int off = offset;  
  6.             char val[] = value;  
  7.   
  8.             for (int i = 0; i < len; i++) {  
  9.                 h = 31*h + val[off++];  
  10.             }  
  11.             hash = h;  
  12.         }  
  13.         return h;  
  14.     }  

注意上面的for循环,有点搞吧?我来举个例子,让你很容易明白它在搞什么名堂。比如有一个字符串“abcde”,采用31进制的计算方法来计算这个字符串的总和,你会写出下面的计算式子:
a*31^4+b*31^3+c*31^2+d*31^1+e*31^0.注意,这里的a,b,c,d或者e指的是它们的ASCII值。很有趣的循环,居然可以用来算N进制。这个循环可以抽出来单独作为计算进制的好工具:

[java] view
plain
copyprint?

  1. public static void main(String[] args) {  
  2.         int[] a={1,0};  
  3.         System.out.println(calculate(2,a));  
  4.     }  
  5.   
  6.     private static int calculate(int radix,int[] a){  
  7.         int sum = 0;  
  8.         for(int i=0;i<a.length;++i){  
  9.             sum = sum*radix+a[i];  
  10.         }  
  11.         return sum;  
  12.     }  

  静态方法caculate接受radix作为进制基数,数组a模拟要计算的进制的数字,只是注意表面顺序需要一致。比如 01 二进制串,在数组中要按照{0,1}排列。上面的输出结果是1,符合01的真实值。
  那么为什么选用31作为基数呢?先要明白为什么需要HashCode.每个对象根据值计算HashCode,这个code大小虽然不奢求必须唯一(因为这样通常计算会非常慢),但是要尽可能的不要重复,因此基数要尽量的大。另外,31*N可以被编译器优化为
左移5位后减1,有较高的性能。其实选用31还是有争议,反对者(参考http://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier)
认为这个东西还是会导致较多的重复,应该用更大的数字。所以,或许将来Java的实现中会有所变化。下面这篇文章介绍了两个结论:
1.基数要用质数
质数的特性(只有1和自己是因子)能够使得它和其他数相乘后得到的结果比其他方式更容易产成唯一性(最终出来的结果只能被素数本身和被乘数还有1来整除),也就是hash code值的冲突概率最小。

2.选择31是观测分布结果后的一个选择,不清楚原因,但的确有利。

3.31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化.(提高算法效率)

http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/

另外,String.hashCode内部会缓存第一次计算的值,因为这是一个final(不可变)类,也就是String对象的内容是不会变的。这能够在多次put到HashMap的场合提高性能,不过似乎用处不多。

时间: 2024-10-22 21:33:32

为什么在定义hashcode时要使用31这个数呢?的相关文章

javascript定义变量时带var与不带var的区别分析

 这篇文章主要介绍了javascript定义变量时带var与不带var的区别,以一个简单实例分析了变量定义时带var与不带var的执行原理及用法区别,需要的朋友可以参考下     本文实例分析了javascript定义变量时带var与不带var的区别.分享给大家供大家参考.具体分析如下: 直接看实例里说明: 代码如下: <script language="javascript" type="text/javascript"> var abc=89;//带

c++-C++宏定义字符串时出错

问题描述 C++宏定义字符串时出错 在用xcode写cocos2d的程序时,遇到一个问题,当调试环境是mac时,需要的文件路径为绝对路径,调试环境是iphone时,文件路径直接是文件名就可以了.为了解决这个问题,尝试写了一下宏.如下: #define RUN_IN_IOS #define TO_STRING(_string) #_string #ifdef RUN_IN_IOS #define GET_FILE(_fileName) TO_STRING(_fileName) #else #def

浅谈JavaScript中定义变量时有无var声明的区别_javascript技巧

前段时间回答了一个关于定义变量时使用关键字var与否的区别,总结回顾一下. 1.在函数作用域内 加var定义的变量是局部变量,不加var定义的就成了全局变量. 使用var定义: var a = 'hello World'; function bb(){ var a = 'hello Bill'; console.log(a); } bb() //'hello Bill' console.log(a); //'hello world' 不使用var定义: var a = 'hello World'

金融-关于datastage定义job时字段长度与数据库表长度不同的问题

问题描述 关于datastage定义job时字段长度与数据库表长度不同的问题 大家好,我是金融IT行业,想咨询一个问题,我在做datastage作业开发时,作业运行出现了警告,现在需要消除警告,就发现了原来是因为此job的输入和输出file stage中的列有一个字段长度和数据库表中的字段长度不一样,数据库表是number(11,8),job中却是Decimal(9,6)这样每次执行job都会警告如下:![图片说明](http://img.ask.csdn.net/upload/201511/2

javascript定义变量时加var与不加var的区别_基础知识

一.外部的为全局,内部的为局部变量. 二.加var为局部变量(在方法内),不加var为全局变量(当方法内有一次使用后) 复制代码 代码如下: <script type="text/javascript"> var golbe="global"; test(); function test(){      var local="local";     document.write(golbe);     document.write(l

javascript定义变量时带var与不带var的区别分析_javascript技巧

本文实例分析了javascript定义变量时带var与不带var的区别.分享给大家供大家参考.具体分析如下: 直接看实例里说明: 复制代码 代码如下: <script language="javascript" type="text/javascript"> var abc=89;//带var,表示全局变量 function test(){  var abc=80;//在函数内部,如果不带var,表示使用函数外全局变量:带上var,表示新定义一个全局变量

javascript面向对象编程:js类定义函数时prototype和this的区别

在面向对象编写js脚本时,定义实例方法主要有两种 如下: function ListCommon2(afirst) { var first=afirst; this.do1=function () { alert("first do"+first); } } ListCommon2.prototype.do2=function() { // alert("first do"+first);//会出错,不能访问first this.do1(); } this.do1=

javascript面向对象编程:js类定义函数时用不用prototype的区别

一直在使用js编写自以为是面向对象的方法,遇到一个问题,就是定义一个方法,如下: function ListCommon2(first,second,third) { this.First=function () { alert("first do"+first); } } ListCommon2.do1=function(first) { // this.First(); alert("first do"+first); } ListCommon2.prototy

JS中定义函数时的参数定义为undefined

问题描述 看到大牛们写的js源码,想拜读学习一下.var KISSY = (function (undefined) { var host = this, S, guid = 0, EMPTY = ''; S = { __BUILD_TIME: '20130701201313', ... }; // exports for nodejs if (S.Env.nodejs) { S.KISSY = S; module.exports = S; } return S;})();请看以上的代码,为什么