条目11:重写equals方法时应该总是重写hashCode方法
为什么要重写hashCode
方法?
Java 的规范明确规定了hashCode
和equals
方法的关系,如果两个对象根据equals
方法是相等的,那么它们的hashCode
值必须相等。这个规定确保了在使用哈希相关的数据结构(如HashMap
、HashSet
等)时,能够正确地工作。如果只重写了equals
方法,而没有重写hashCode
方法,就会导致程序在处理哈希集合时出现异常行为。
如果只重写equals
会发生什么?
会出现问题的代码示例:
java
import java.util.HashSet;
import java.util.Objects;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
// 注意:未重写hashCode方法
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}
public class Main {
public static void main(String[] args) {
HashSet<Person> people = new HashSet<>();
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Alice", 25);
// 添加两个逻辑上相等的对象
people.add(person1);
people.add(person2);
// 打印集合
System.out.println(people);
}
}
运行结果:
java
[Person{name='Alice', age=25}, Person{name='Alice', age=25}]
虽然person1
和person2
在逻辑上是相等的(equals
方法返回true
),但由于hashCode
未被重写,它们的哈希值是不同的(基于默认实现Object.hashCode
)。因此,HashSet
无法正确识别它们是同一个对象。
重写equals
和hashCode
方法
java
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}
public class Main {
public static void main(String[] args) {
HashSet<Person> people = new HashSet<>();
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Alice", 25);
// 添加两个逻辑上相等的对象
people.add(person1);
people.add(person2);
// 打印集合
System.out.println(people);
}
}
运行结果:
java
[Person{name='Alice', age=25}]
hashCode
的实现原则
- 一致性原则 。在同一程序执行期间,如果一个对象的状态没有改变,则其
hashCode
值必须始终一致。 - 相等性原则 。如果两个对象根据
equals
方法是相等的,那么它们的hashCode
值必须相等。 - 高效性原则。将对象尽可能均匀地分布在哈希表中,以减少哈希冲突。
实现方式:
-
使用
Objects.hash()
方法,这是最简单的方式。 -
手动组合关键字段,确保分布均匀。
java@Override public int hashCode() { int result = 17; // 初始值 result = 31 * result + (name == null ? 0 : name.hashCode()); result = 31 * result + age; return result; }