这是Java中经典的问题,在面试中也经常被问起.很多书提到要重载hashCode()和equals()两个方法才能实现自定义键在HashMap中的查找,但是为什么要这样以及如果不这样做会产生什么后果,好像很少有文章讲到,所以来这一篇记录下.
首先,如果我们直接用以下的Person类作为键,存入HashMap中,会发生发生什么呢?
package com.csdn;
/**
* @author Shusheng Shi
* @since 2017/6/4 11:49
*/
public class Person {
private String id;
public Person(String id) {
this.id = id;
}
}
package com.csdn;
import java.util.HashMap;
/**
* @author Shusheng Shi
* @since 2017/6/4 16:03
*/
public class Main {
public static void main(String[] args) {
HashMap<Person, String> map = new HashMap<>();
map.put(new Person("001"), "findingsea");
map.put(new Person("002"), "linyin");
map.put(new Person("003"), "henrylin");
map.put(new Person("003"), "findingsealy");
System.out.println(map.toString());
System.out.println(map.get(new Person("001")));
System.out.println(map.get(new Person("002")));
System.out.println(map.get(new Person("003")));
}
}
那么输出结果是什么呢?
{com.csdn.Person@74a14482=henrylin, com.csdn.Person@4554617c=linyin, com.csdn.Person@1b6d3586=findingsea, com.csdn.Person@1540e19d=findingsealy}
null
null
null
我们可以看到,这里出现了两个问题:
那么,正确的方法是直接对Person类进行修改,重写equals和hashCode方法,修改过后的Person类如下:
package com.csdn;
/**
* @author Shusheng Shi
* @since 2017/6/4 11:49
*/
public class Person {
private String id;
public Person(String id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
return o instanceof Person && (id == ((Person) o).id);
}
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
尽管看起来equals()方法只是检查其参数是否为Person的实例,但是instanceof悄悄地检查了此对象是否为null,因为若instance左边参数为null,它会返回false.若参数不为null,且类型正确,则基于每一个对象中实际的id值的hashCode进行比较.从输出结果也看出,这种方式是正确的.
{com.csdn.Person@ba31=findingsea, com.csdn.Person@ba32=linyin, com.csdn.Person@ba33=findingsealy}
findingsea
linyin
findingsealy
可以看到,之前指出的错误都得到了改正.为什么会这样呢? 在HashMap中,查找key的比较顺序为:
显然,第一步就是要用到hashCode()方法,而第二步就是要用到equals()方法.在没有进行重载时,这两步会默认调用Object类的这两个方法.
而在Object类中Hash Code默认是使用对象的地址计算的,那两个Person(“003”)的对象地址是不同的,所以它们的Hash Code也不同,自然HashMap也不会把它们当成是同一个key了.同时,在Object默认的equals()中,也是根据对象的地址进行比较,自然一个Person(“003”)和另一个Person(“003”)是不相等的.
理解了这一点,就很容易搞清楚为什么需要同时重载hashCode()和equals两个方法了.
还有一个细节,在Person类中对于hashCode()的重在方法为:
@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
这里可能有疑惑的点在于:为什么可以用String类型的变量的Hash Code作为Person类的Hash Code值呢?这样new Person(new String(“003”))和new Person(new String(“003”))的Hash Code是相等的吗?
来看看以下代码的输出:
728795174
728795174
728795174
728795174
可以看到四条语句的输出都是相等的,很直观的合理的猜测就是String类型也重载了hashCode()以根据字符串的内容来返回Hash Code值,所以相同内容的字符串具有相同的Hash Code.
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()相等的情况下,还需要用equals()进行比较呢?就是因为避免出现上述例子中的出现的情况,因为根据对Person类的hashCode()方法的重载实现,Person类会直接用id这个String类型成员的Hash Code值作为自己的Hash Code值,但是很显然的,一个Person(“003”)和一个String(“003”)是不相等的,所以在hashCode()相等的情况下,还需要用equals()进行比较.
以下例子可以作为上述说明的佐证:
System.out.println(new Person("003").hashCode()); // 47667
System.out.println(new String("003").hashCode()); // 47667
System.out.println(new Person("003").equals(new String("003"))); // false
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有