讲一下equals()与hashcode(),什么时候重写,为什么重写,怎么重写?
参考回答
equals()
和 hashCode()
是 Java 中 Object
类定义的两个重要方法,用于对象比较和哈希操作。
equals()
:用于判断两个对象是否“内容相等”。hashCode()
:返回对象的哈希值,用于哈希表等数据结构中快速查找。
当需要自定义对象的比较逻辑(例如,两个对象的内容相等但引用不同),就需要同时重写 equals()
和 hashCode()
,并确保它们的逻辑保持一致性。
详细讲解与拓展
1. equals()
的默认实现
Object
类中 equals()
的默认实现是使用引用比较,即判断两个对象是否是同一个内存地址。例如:
public class Test {
public static void main(String[] args) {
String s1 = new String("Hello");
String s2 = new String("Hello");
System.out.println(s1.equals(s2)); // true,因为 String 重写了 equals()
System.out.println(s1 == s2); // false,因为引用不同
}
}
如果你的类不重写 equals()
,调用时只会比较引用,而不是对象内容。
2. hashCode()
的默认实现
Object
类中 hashCode()
的默认实现是根据对象的内存地址计算的一个整数值。这意味着两个“内容相等”的对象可能有不同的哈希值,除非重写了 hashCode()
。
3. equals()
与 hashCode()
的关系
Java 规定了 equals()
和 hashCode()
的关系:
- 如果两个对象通过
equals()
判断相等,则它们的hashCode()
值必须相等。 - 如果两个对象
hashCode()
值相等,它们通过equals()
不一定相等。
这个关系的重要性体现在哈希表类(如 HashMap
、HashSet
)的工作机制中。
4. 什么时候需要重写 equals()
和 hashCode()
- 自定义对象用于集合中(
HashMap
、HashSet
等):- 如果没有重写
equals()
和hashCode()
,集合会将“内容相等”的对象当作不同对象处理。
- 如果没有重写
- 需要基于对象的内容进行比较:
- 例如,在比较两个人的身份证号是否相等时,需要通过
equals()
判断内容,而不是引用。
- 例如,在比较两个人的身份证号是否相等时,需要通过
5. 如何重写 equals()
和 hashCode()
假设你有一个类 Person
,需要根据 id
和 name
判断对象是否相等:
public class Person {
private int id;
private String name;
// 构造方法、Getter 和 Setter
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // 引用相等
if (obj == null || getClass() != obj.getClass()) return false; // 类型检查
Person person = (Person) obj; // 类型转换
return id == person.id && name.equals(person.name); // 比较内容
}
@Override
public int hashCode() {
int result = Integer.hashCode(id); // 基于 id 的哈希值
result = 31 * result + name.hashCode(); // 组合 name 的哈希值
return result;
}
}
关键点:
-
equals():
- 检查引用相等。
- 检查类型是否一致。
- 比较对象的内容。
-
hashCode():
- 使用对象的关键字段生成哈希值。
- 避免硬编码(
31
是常用的乘数,因为它是一个奇质数)。
6. 重写的原则
- 一致性:
- 如果
equals()
判断两个对象相等,它们的hashCode()
必须相等。 - 如果对象在应用程序生命周期中没有被修改,
equals()
和hashCode()
的结果也必须一致。
- 如果
- 性能:
hashCode()
的实现应尽量均匀分布,避免产生过多冲突。
7. 举例:重写的重要性
如果不重写 hashCode()
和 equals()
,以下代码会有问题:
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
Person p1 = new Person(1, "Alice");
Person p2 = new Person(1, "Alice");
HashSet<Person> set = new HashSet<>();
set.add(p1);
set.add(p2);
System.out.println(set.size()); // 如果不重写 equals 和 hashCode,输出 2;否则输出 1
}
}
8. 拓展知识
Objects
工具类:- Java 提供了 Objects.equals() 和 Objects.hash(),可以简化 equals() 和 hashCode()的实现。例如:
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Person person = (Person) obj; return id == person.id && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(id, name); }
- Lombok 的帮助:
- 使用 Lombok,可以通过注解
@EqualsAndHashCode
自动生成这两个方法,避免手写代码。
- 使用 Lombok,可以通过注解
hashCode()
的优化:- 对于大型集合中的频繁查询,设计一个高效的
hashCode()
函数可以显著提升性能。
- 对于大型集合中的频繁查询,设计一个高效的
9. 总结
- 重写
equals()
和hashCode()
是为了让对象的比较逻辑更贴近业务需求,尤其是在集合类中使用时。 - 两者必须保持一致性,即两个对象相等时它们的哈希值也必须相等。
- 重写时,应遵守规范并尽量使用工具类来简化实现。