1.是什么
hashCode()
和 equals()
是 Java 中两个非常重要的基础方法,主要用于对象的比较和哈希相关操作。它们的关系在于:如果两个对象根据 equals()
方法相等,那么它们的 hashCode()
必须相同,但反过来并不一定成立。理解它们的关系非常关键,特别是在使用哈希表数据结构(如 HashMap
、HashSet
)时。
1. equals()
方法
equals()
方法用于比较两个对象是否相等。默认情况下,它比较的是对象的引用(即内存地址),但通常我们会根据对象的实际内容来重写这个方法。例如,在 Person
类中,我们可能会根据 name
和 age
属性来判断两个 Person
对象是否相等。
2. hashCode()
方法
hashCode()
方法返回对象的哈希码值,这是一个整数。哈希码用于确定对象在哈希表中的索引位置。理想情况下,不同的对象应该产生不同的哈希码,但这并不是强制的。重要的是,如果两个对象通过 equals()
方法比较是相等的,那么它们的 hashCode()
方法必须返回相同的整数值。
3.hashCode()
和equals()
的关系
-
等价性(Equality):
- 如果两个对象根据
equals()
方法是相等的,那么这两个对象必须具有相同的哈希码。即o1.equals(o2)
为true
时,o1.hashCode()
必须与o2.hashCode()
返回相同的整数值。 - 这是因为基于哈希的集合(如
HashMap
)使用hashCode()
来快速定位对象所在的桶(bucket),然后使用equals()
来检查对象是否真的相等。
- 如果两个对象根据
-
不一致性(Inconsistency):
- 如果两个对象的
hashCode()
返回相同的值,这并不意味着这两个对象必须相等。不同的对象可能有相同的哈希码,这就是所谓的哈希冲突。 - 但是,如果两个对象的
hashCode()
返回不同的值,那么这两个对象一定不相等。
- 如果两个对象的
总结:
- 如果两个对象相等(即
equals()
返回true
),则它们的hashCode()
值必须相等。否则,基于哈希的集合(如HashMap
、HashSet
)在存储和查找对象时会发生错误。 - 如果两个对象的
hashCode()
值相等,它们不一定相等(即equals()
不一定返回true
)。但这样会导致哈希表中不同对象落入同一个“桶”,从而可能影响查找性能。
4.为什么要重写 hashCode()
和 equals()
equals() ⽅法⽤于⽐较两个对象的内容是否相等。在Java中,默认实现是⽐较对象的引⽤,即⽐较两个对象是否指向内存中的相同位置。但通常,我们希望⽐较对象的内容是否相等。鉴于这种情况,Object类中 equals() ⽅法的默认实现是没有实⽤价值的,所以通常都要重写。⽽由于hashCode()与equals()具有联动关系(如果两个对象相等,则它们必须有相同的哈希码),所以equals()⽅法重写时,通常也要将hashCode()进⾏重写,使得这两个⽅法始终保持⼀致性。
代码示意:
不重写 hashCode()
和 equals()
方法
默认情况下,equals()
和 hashCode()
是基于对象的内存地址比较的:
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("John", 25);
Person p2 = new Person("John", 25);
System.out.println(p1.equals(p2)); // 输出:false,默认比较的是内存地址
System.out.println(p1.hashCode() == p2.hashCode()); // 输出:false,hashCode 基于内存地址
}
}
重写 equals()
但不重写 hashCode()
如果你只重写了 equals()
而没有重写 hashCode()
,则会出现问题:即使两个对象根据 equals()
相等,它们的 hashCode()
可能不同。这会导致哈希表无法正确处理它们。
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("John", 25);
Person p2 = new Person("John", 25);
System.out.println(p1.equals(p2)); // 输出:true,内容相同
System.out.println(p1.hashCode() == p2.hashCode()); // 输出:false,hashCode 不相等
// 放入 HashSet 中
HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
System.out.println(set.size()); // 输出:2,应该是1(因为 equals 相等),但 hashCode 不同导致重复
}
}
重写 equals()
和 hashCode()
如果我们重写了 equals()
方法,还必须重写 hashCode()
方法来保证相等的对象有相同的哈希值。这样,哈希表会正确处理这些对象。
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
@Override
public int hashCode() {
return name.hashCode() + age; // 基于对象的属性计算 hashCode
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("John", 25);
Person p2 = new Person("John", 25);
System.out.println(p1.equals(p2)); // 输出:true
System.out.println(p1.hashCode() == p2.hashCode()); // 输出:true
// 放入 HashSet 中
HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
System.out.println(set.size()); // 输出:1,说明 p1 和 p2 被认为是相同的对象
}
}
5. 总结
- equals() 用于比较两个对象是否相等,通常基于对象的内容来比较。
- hashCode() 用于生成对象的哈希值,主要用于提高基于哈希表数据结构的效率。
- 如果重写了
equals()
,务必也要重写hashCode()
,以确保相等的对象拥有相同的哈希值,从而保证哈希表中的正常操作。 - 如果两个对象
equals()
返回true
,它们的hashCode()
必须相同;但hashCode()
相同的对象不一定equals()
返回true
。