Java中hashCode和equals的关系
什么是hashCode
hashCode是对象的散列值,也叫哈希值。默认情况下是根据对象的存储地址进行哈希映射得到的一个整数值。散列值可以提高查找的效率,主要应用于散列存储结构中快速确定对象的存储位置,如HashSet、HashMap。
比如HashSet,它的内部采用对某个数字n进行取余的方式对散列值进行分组和划分对象的存储区域。当从HashSet集合中查找某个对象时,先调用对象的hashCode方法获得该对象的散列值,然后根据散列值找到相应的存储区域,最后调用equals,遍历该存储区域内的每个元素与该对象逐一比对,一个优秀的哈希算法能大大减少查找的时间开销。
equals和“==”的区别
JDK中Object类自带的equals方法作用和“==”一致,都是简单的判断两个对象的地址是否相同。但在实现具体的类时,可以覆盖父类的equals方法,来实现自己想要的功能,比如比较两个对象内容是否相等。
hashCode和equals的关系
hashCode方法主要是用于查找,而equals方法主要是用于两个对象的比较。一般来说只有在散列存储结构中才会用到hashCode方法。比如HashSet的contains方法,要判断集合中是否含有某个对象,它会先调用hashCode方法,查找若得到的哈希值相同,才会继续调用equals方法比较这两个对象是否相等。
如果你只覆盖了某个类的equals,那么当你想要使用HashSet时,集合中可能会出现两个内容相同的对象。因此,当你想覆盖euqals方法的时候,也要覆盖hashCode方法,这是一个好习惯。
重写方法时要注意的问题
当我们需要重写这两个方法时,要注意以下三点:
(1)若两个对象equals结果为true,则其hashCode结果应该相同;
(2)两个对象的hashCode相同,不代表两个对象的equals结果为true;
(3)如果你重写了equals,那么你也应该重写hashCode。
另外,JAVA的基本数据类型和String类重写了父类的hashCode和equals。对于基本数据类型,hashCode就是简单的返回数值,equals就是比较数值的大小;对于String类,它通过一种复杂的计算来实现hashCode和equals是等价的,当且仅当两个字符串的内容相同。
重写hashCode带来的问题
我们来看看下面这个例子。我重写了Person类的hashCode方法,将一个Person类的对象加入到HashSet后,修改它fields的取值后,调用contains方法显示该对象并不在集合中。为什么会出现这种情况?原因是显然的,我们修改了name的取值,导致这个对象的散列值发生变化,这时候HashSet再调用hashCode查找时,可能就找不到了。
class Person{
private String name;
public Person(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override public int hashCode() {
int len = name.length(), hashcode=0;
for(int i=0; i<len; i++) {
hashcode += name.charAt(i);
}
return hashcode;
}
}
Set<Person> set = new HashSet<Person>();
Person p = new Person("hashCode");
set.add(p);
p.setName("Change");
System.out.println(set.contains(p));//false
因此,如果你的类重写了hashCode方法,且类的字段参与了散列值的计算,那么我们就不应该轻易修改它的字段。