题目
如何正确重写equals()和hashCode()方法?
信息
- 类型:问答
- 难度:⭐⭐
考点
对象相等性,集合框架,方法重写规范
快速回答
正确重写equals()和hashCode()需要遵循以下原则:
- equals()规范:满足自反性、对称性、传递性、一致性和非空性
- hashCode()规范:相等对象必须有相同哈希码,不相等对象尽量不同
- 同步重写:重写equals()时必须同时重写hashCode()
- 使用关键字段:计算应基于equals()比较中使用的字段
1. 原理说明
在Java中,equals()定义对象逻辑相等性,hashCode()为对象生成哈希值用于散列存储。两者必须协同工作:
- 当两个对象
equals()返回true时,它们的hashCode()必须相同 - 哈希冲突时(不同对象相同哈希码),集合会使用
equals()进一步验证 - 违反约定会导致
HashMap、HashSet等集合行为异常
2. 代码示例
public class Person {
private String name;
private int age;
// 构造方法省略
@Override
public boolean equals(Object o) {
// 1. 地址相同
if (this == o) return true;
// 2. 非空且类型相同
if (o == null || getClass() != o.getClass()) return false;
// 3. 字段比较
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
// 使用相同字段计算哈希码
return Objects.hash(name, age);
}
}3. 最佳实践
- 使用
Objects.equals()比较字段避免NPE - 优先使用
Objects.hash()计算哈希码 - 对不可变对象可缓存哈希码值
- 在子类中通过
super.equals()包含父类字段
4. 常见错误
- 错误1:只重写
equals()不重写hashCode()// 导致相同对象在HashMap中存储两次 Map<Person, String> map = new HashMap<>(); Person p1 = new Person("Alice", 30); Person p2 = new Person("Alice", 30); map.put(p1, "value1"); map.put(p2, "value2"); // 预期覆盖但实际新增条目 - 错误2:使用可变字段参与计算
// 对象放入HashSet后修改字段 Set<Person> set = new HashSet<>(); Person p = new Person("Bob", 25); set.add(p); p.setAge(30); // 修改后hashCode变化,导致无法检索 - 错误3:忽略
null检查// 未检查参数类型直接转型 public boolean equals(Object o) { Person other = (Person) o; // 可能抛出ClassCastException // ... }
5. 扩展知识
- Lombok简化:使用
@EqualsAndHashCode注解自动生成 - 性能优化:对计算代价高的哈希码进行缓存
private int hash; // 默认0 @Override public int hashCode() { int h = hash; if (h == 0) { h = Objects.hash(name, age); hash = h; } return h; } - 继承处理:子类重写时需包含父类字段
@Override public boolean equals(Object o) { if (!super.equals(o)) return false; // 比较子类特有字段... } - 集合影响:
HashMap通过(hash & (n-1))确定桶位置,哈希冲突会降低性能