Object类的hashCode方法默认返回对象的内存地址。
hashCode() 的作用是获取哈希码,也称为散列码;它返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建并某个“类的散列表”时,该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),类的hashCode() 没有作用。
上面的散列表,指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
首先我们用散列函数将给定键转化为一个“数组的索引”,理想情况下,不同的key会被转为不同的索引,但在实际应用中我们会遇到不同的键转为相同的索引的情况,这种情况叫做碰撞。
得到了索引后,我们就可以像访问数组一样,通过这个索引访问到相应的键值对。
在散列表内部,我们使用桶(bucket)来保存键值对,数组索引即为桶号,决定了给定的键存于散列表的哪个桶中。散列表所拥有的桶数被称为散列表的容量(capacity)。
现在假设我们的散列表中有M个桶,桶号为0到M-1。我们的散列函数的功能就是把任意给定的key转为[0, M-1]上的整数。我们对散列函数有两个基本要求:一是计算时间要短,二是尽可能把键分布在不同的桶中。对于不同类型的键,我们需要使用不同的散列函数,这样才能保证有比较好的散列效果。
String类的hashCode方法
public int hashCode() {
int h = hash; //hash默认是0
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;
}
在方法开始会把hash赋给h,这个hash就表示之前计算的hashCode,这样以来若之前已经计算过这个字符串对象的hashCode,这次我们就无需再计算了,直接返回之前计算过得即可。这种把hashCode缓存的策略只对不可变对象有效(String因此不可变),因为不可变对象的hashCode是不会变的。
Integer类的hashCode方法仅仅是简单的返回了自身的值。
Double类的hashCode方法首先会将它的值转为long类型,然后返回低32位和高32位的异或的结果作为hashCode。
对于等价的两个对象(也就是调用equals方法返回true),它们的hashCode必须相同,而反之则不然。
由hashCode获取桶号
一个直接的办法就是直接拿得到的hashCode除以capacity(桶的数量),然后用所得的余数作为桶号。不过在Java中,hashCode是int型的,而Java中的int型均为有符号,所以我们要是直接使用返回的hashCode的话可能会得到一个负数,显然桶号是不能为负的。所以我们先将返回的hashCode转变为一个非负整数,再用它除以capacity取余数,作为key的对应桶号.
private int hash(K key) { return (x.hashCode() & 0x7fffffff) % M;
}