天天看点

关于hashcode 里面 使用31 系数的问题

首先我们来了解一下hashcode,什么是hashcode?有什么作用?

hashcode其实就是散列码,使用hashcode使用高效率的哈希算法来定位查找对象!

我们在使用容器来存储数据的时候会计算一串散列码,然后将数据放入容器。

如:string s =“java”,那么计算机会先计算散列码,然后放入相应的数组中,数组的索引就是从散列吗计算来的,然后再装入数组里的容器里,如list.这就相当于把你要存的数据分成了几个大的部分,然后每个部分存了很多值, 你查询的时候先查大的部分,再在大的部分里面查小的,这样就比先行查询要快很多!

<a></a>

一个对象的hashcode就是一个简单的hash算法的实现,虽然它和那些真正的复杂的!hash算法相比还不能叫真正的算法,但如何实现它,不仅仅是程序员的编程水平问题, 而是关系到你的对象在存取时性能的非常重要的问题.有可能,不同hashcode可能 会使你的对象存取产生成百上千倍的性能差别!

java string在打印这个类型的实例对象的时候总是显示为下面的形式

test.test$tt@c17164

上面test.test是类名$tt是我自己写的内部类,而@后面这一段是什么呢?他其实就是tt这个实例类的hashcode是的16进制!

它使用了object 里面的tostring()方法

java代码

return getclass().getname() + “@” + integer.tohexstring(hashcode()); 

继续看看java里 string hashcode的源码:

public int hashcode() {

int h = hash;

int len = count;

if (h == 0 &amp;&amp; len &gt; 0) {

int off = offset;

char val[] = value;

for (int i = 0; i &lt; len; i++) {

h = 31*h + val[off++];

}

hash = h;

return h;

其实上面的实现也就是数学里:

s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1] 

的实现!我们会注意那个狗血的31这个系数为什么总是在里面乘来乘去?为什么不适用32或者其他数字?

大家都知道,计算机的乘法涉及到移位计算。当一个数乘以2时,就直接拿该数左移一位即可!选择31原因是因为31是一个素数!

所谓素数:

质数又称素数

。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。

素数在使用的时候有一个作用就是如果我用一个数字来乘以这个素数,那么最终的出来的结果只能被素数本身和被乘数还有1来整除!如:我们选择素数3来做系数,那么3*n只能被3和n或者1来整除,我们可以很容易的通过3n来计算出这个n来。这应该也是一个原因!

在存储数据计算hash地址的时候,我们希望尽量减少有同样的hash地址,所谓“冲突”。如果使用相同hash地址的数据过多,那么这些数据所组成的

hash链就更长,从而降低了查询效率!所以在选择系数的时候要选择尽量长的系数并且让乘法尽量不要溢出的系数,因为如果计算出来的hash地址越大,所

谓的“冲突”就越少,查找起来效率也会提高。

31可以 由i*31== (i&lt;&lt;5)-1来表示,现在很多虚拟机里面都有做相关优化,使用31的原因可能是为了更好的分配hash地址,并且31只占用5bits!

在java乘法中如果数字相乘过大会导致溢出的问题,从而导致数据的丢失.

而31则是素数(质数)而且不是很长的数字,最终它被选择为相乘的系数的原因不过与此!

<a href="http://www.cogs.susx.ac.uk/courses/dats/notes/html/node114.html" target="_blank">http://www.cogs.susx.ac.uk/courses/dats/notes/html/node114.html</a>

本文来源于"阿里中间件团队播客",原文发表时间" 2012-03-19"