总结 equals 和 hashCode
这篇文章主要是总结自: Java hashCode() 和 equals()的若干问题解答
概述
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则一个 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
该方法主要是为了支持如 HashSet 、 HashMap 等基于哈希表实现的集合。
也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
讨论 equals() 和 hashCode() 的关系应该分两种情况进行:
不会创建“类对应的散列表”
第一种是不会在 HashSet, Hashtable, HashMap 等基于哈希表实现的数据结构中用到该类,这种情况下 equals() 和 hashCode() 没有关系,如果要比较对象,只需使用 equals() 来进行比较,而并不需要用到 hashCode() ,所以重写 equals() 时不需要重写 hashCode()。
比如只是要比较两个 Person 对象的 name 是否相同,但没有要将其放到 HashSet 等集合,只需重写 equals() 即可,如:
(来自参考链接)
public boolean equals(Object obj){
if(obj == null){
return false;
}
//如果是同一个对象返回true,反之返回false
if(this == obj){
return true;
}
//判断是否类型相同
if(this.getClass() != obj.getClass()){
return false;
}
Person person = (Person)obj;
return name.equals(person.name);
}
会创建“类对应的散列表”
第二种情况则是会在 HashSet, Hashtable, HashMap 等基于哈希表实现的数据结构中用到该类。
这时需要注意两者的关系:
-
若是两个对象使用 equals() 比较返回 true,那么使用 hashCode() 应该返回相同的哈希值。否则会出现 “ 两个对象是相同的(equals 返回 true),但却可以放进同一个哈希表 ” 的情况。
hashCode() 的 javaDoc :If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
-
但如果使用 equals() 比较返回 false ,并不要求 hashCode() 返回不同的值。因为在哈希表中,即使两个哈希值相同(两个 key 相同),它们的值不一定相同(value 不一定相同),这种情况即哈希碰撞。
hashCode() 的 javaDoc :It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results
一个要注意的点是:在 HashSet 、 HashMap 等基于哈希表的集合中,若是两个对象使用 equals() 比较返回 false,但 hashCode() 返回相同的值(即产生哈希碰撞时),还是会将两个对象都放进集合(这时会使用哈希碰撞解决方法,如拉链法,红黑树等)。
所以若是要在哈希表实现的数据结构中使用到某个类,则需要重写这个类的 equals() 和 hashCode() ,并保证它们满足上述关系。
示例:(来自参考链接)
定义 Person 类:
class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " - " +age;
}
@Override
public int hashCode(){
// 两个对象的 name 的大写相同且年龄相同时,返回相同的哈希值
int nameHash = name.toUpperCase().hashCode();
return nameHash ^ age;
}
@Override
public boolean equals(Object obj){
if(obj == null){
return false;
}
//如果是同一个对象返回true,反之返回false
if(this == obj){
return true;
}
//判断是否类型相同
if(this.getClass() != obj.getClass()){
return false;
}
// name 相同(区分大小写)且年龄相同返回 true
Person person = (Person)obj;
return name.equals(person.name) && age==person.age;
}
}
测试:
public static void main(String[] args) {
// 新建Person对象,
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
Person p3 = new Person("aaa", 200);
Person p4 = new Person("EEE", 100);
// 新建HashSet对象
HashSet set = new HashSet();
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
// 比较p1 和 p2, 并打印它们的hashCode()
System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n",
p1.equals(p2), p1.hashCode(), p2.hashCode());
// 比较p1 和 p4, 并打印它们的hashCode()
System.out.printf("p1.equals(p4) : %s; p1(%d) p4(%d)\n",
p1.equals(p4), p1.hashCode(), p4.hashCode());
// p1.equals(p2) : true; p1(68545) p2(68545)
// p1.equals(p4) : false; p1(68545) p4(68545)
// 打印set,可以看到 p1 p4 equals返回 false,但 hashcode 相同,所以都被放进集合
System.out.printf("set:%s\n", set);
// set:[eee - 100, EEE - 100, aaa - 200]
}
如果不重写 hashCode() ,那么 p1、p2 都可以被放进 set 。

浙公网安备 33010602011771号