必须同步重写equals和hashCode,否则破坏哈希契约:若equals为true则hashCode必须相同,否则HashMap等集合会查找不到、重复添加或丢失元素;二者字段需一致、顺序相同且稳定。
重写
和
必须同步进行,否则会破坏哈希契约,导致对象在
、
等集合中行为异常——比如明明相等的对象查不到、重复添加、甚至集合“丢失”元素。
为什么必须一起重写?
Java 规定:如果两个对象通过
判断为
,那么它们的
值也必须相同。这是哈希集合(如
)正常工作的前提:
先用
定位桶(bucket),再用
在桶内精确比对;
若
返回
但
不同 → 两个对象被散列到不同桶 →
或
找不到;
若只重写
不重写
→ 默认使用
(基于内存地址),逻辑不一致,同样出错。
重写时的关键原则
确保语义一致、稳定、高效:
一致性
:只要用于比较的字段没变,多次调用
必须返回相同值;
对称性
:若
为
,则
也必须为
;
传递性与非空性
:
方法不能对
抛
,推荐用
安全比较;
只用“业务关键字段”参与计算
:和
判定逻辑完全一致的字段,才应参与
计算(例如
和
,而不是
或临时缓存字段)。
标准写法示例(JDK 7+ 推荐)
以一个简单的
类为例:
Eclipse导入Android或其他的JAVA项目的正确方法 WORD版
本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
下载
立即学习
“
Java免费学习笔记(深入)
”;
注意
:
内部按参数顺序调用各字段的
,所以字段顺序要和
中的比较顺序严格一致;若字段可能为
,
和
都能安全处理。
常见错误与避坑提醒
重写了
却忘了
注解 → 编译器不报错,但实际没覆盖父类方法;
在
中用了可变字段(如普通
字段且未声明
),对象放入
后修改该字段 →
改变 → 桶位置失效 → 无法被找到;
用随机数、当前时间、数据库自增 ID(未持久化前)等不稳定值参与
计算;
在
中比较了子类特有字段,但父类的
没覆盖 → 违反对称性或哈希契约。
equalshashCodeHashMapHashSetequalstruehashCodeHashMapHashMaphashCodeequalsequalstruehashCodeget()contains()hashCodeequalsObject.equalshashCodea.equals(b)trueb.equals(a)trueequalsnullNullPointerExceptionObjects.equals(a, b)equalshashCodeidnamecreateTimeUserpublic class User {
private final String id;
private final String name;
private int age; // 注意:若 age 参与 equals,则必须参与 hashCode;若不参与,就别比它
public User(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name); // 自动处理 null,顺序必须和 equals 中一致
}
}Objects.hash(...)hashCodeequalsnullObjects.equalsObjects.hashequals@OverridehashCodeagefinalHashSethashCodehashCodeequalshashCode