如果两个对象通过调用equals方法是相等的,那么这两个对象调用hashCode方法必须返回相同的整数。
面试时很容易碰到的问题: equals方法与 == 运算符的区别, 许多人都会回答:equals比较的是对象的内容,而 == 比较的是对象的地址. 这种说法其实并不完全正确, 在 Object 类中 equals 方法的实现如下所示,可以看到其实在超类Object中就是使用 == 运算符来比较两个对象是否相等的.
public boolean equals(Object var1) {
return this == var1;
}
equals比较的是对象的内容 这个印象是Object 子类重写这个方法后带给我们的. 在超类Object中, equals 其实也是直接比较内存地址,而 hashCode 是基于内存地址生成的,故此时必然符合规定.
Object子类通常会重写equals方法,其方式大多是根据内部属性进行比较校验来完成相等判断.这时候会发生一个现象,即两个对象的内存地址不相同但是具有相同的属性值,这时equals方法也会返回true.此时如果不重写 hashCode 方法而直接调用Object类方法,就会因内存地址不同而造成 hashCode 返回值不一致,从而违反了Java 规定.
hashCode 在 Map 集合的使用中特别频繁. 以 HashMap 为例, 在get 或者put 键值对的时候,都需要通过hash 方法计算key的hasCode, 从而计算该键值对在数组结构中的 index 下标. 在 HashMap的 hash 方法中存在对 key == null 的处理,故其允许 null 键的存在.
static final int hash(Object var0) {
int var1;
return var0 == null ? 0 : (var1 = var0.hashCode()) ^ var1 >>> 16;
}
HashTable 中计算 hashCode 是直接调用key.hashCode(), 该调用在key 为null 时可以想见必然会报出 NPE, 故 HashTable 不允许null 键 null 值.
public synchronized V put(K var1, V var2) {
if (var2 == null) {
throw new NullPointerException(); // null 值抛出NPE.
} else {
Hashtable.Entry[] var3 = this.table;
int var4 = var1.hashCode();// null 键直接调用 hashCode() 方法, NPE.
.......
}
当我们以重写了equals方法而没有重写 hashCode方法的类对象作为key在Map集合中进行存储时(如Map.put(a,1)),会发现尽管存在 a.equals(b) == true, 但是 Map.get(b) 返回的并不是期望值1, 很大可能是 null. 这就是因为a 和 b 的hasCode 不一致,从而造成index下标定位不准的问题.
String类中的hashCode计算方法比较简单,就是以31为权取每一位字符的ASCII值进行运算.
以31为系数是因为31是一个奇质数,所以 (31 * i)=(32 -1 )* i =( i<<5 ) - i ,这种位移与减法结合的计算相比一般运算更快。
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;
}
链接: [1]https://blog.csdn.net/javazejian/article/details/51348320