1. 等价关系 (Equivalence Relation)
- 定义:等价关系E是满足以下条件的T x T的子集:
- 自反性 (Reflexive):对所有T中的t,E(t, t)
- 对称性 (Symmetric):如果E(t, u),则E(u, t)
- 传递性 (Transitive):如果E(t, u)且E(u, v),则E(t, v)
- 应用:使用等价关系E定义相等性,即a等于b当且仅当E(a, b)。
2. 等价性的三种考量方式
- 抽象函数 (AF):将具体实例映射到相应的抽象值,a等于b当且仅当AF(a) = AF(b)。
- 观察者角度:如果两个对象在所有可应用的操作下产生相同的结果,则它们被视为等价。
- == 与 equals():
==
用于基本数据类型,检查引用等价性。equals()
用于对象类型,检查对象内容等价性。
3. 实现 equals()
- 默认行为:Object类中的equals()默认实现检查引用等价性。
- 重写需求:对于不可变类型,通常需要重写equals()以实现基于抽象值的比较。
- 注意事项:
- 使用
@Override
注解确保正确重写。 - 避免使用
instanceof
,除非在实现equals()时。
- 使用
这里具体展开来看一下equal:
实现equals()
-
equals() 方法的重要性:
- 在Java中,equals() 方法用于比较两个对象的内容是否相等,即对象等价性(object equality)。
- 对于自定义的抽象数据类型(ADT),需要适当地定义equals(),以确定对象间何时视为等价。
-
默认的equals() 实现:
- 在
Object
类中,equals() 默认实现比较的是引用等价性(referential equality),即两个引用是否指向内存中的同一位置。
- 在
-
重写equals():
- 对于不可变类型(immutable types),通常需要重写equals(),以实现基于抽象值的比较。
- 重写时,应确保equals() 遵循等价关系的所有性质:自反性(reflexive)、对称性(symmetric)、传递性(transitive)。
-
使用
@Override
注解:- 使用
@Override
注解来重写方法时,编译器会检查父类中是否存在相同签名的方法,确保正确重写。
- 使用
-
equals() 的一致性:
- 重写equals() 时,必须保证在对象未被修改的情况下,多次调用equals() 返回的结果一致。
-
equals() 与
instanceof
:- 在equals() 方法中使用
instanceof
是允许的,用于检查对象是否是特定类型的实例。 - 但在面向对象编程中,通常应避免使用
instanceof
,因为它破坏了多态性。
- 在equals() 方法中使用
-
equals() 与
hashCode()
:- 当重写equals() 时,应同时重写
hashCode()
,以确保两个通过equals() 比较相等的对象,调用hashCode()
时返回相同的整数值。 - 这是为了保持使用哈希表的数据结构(如HashSet和HashMap)的正确性。
- 当重写equals() 时,应同时重写
-
hashCode() 的实现:
hashCode()
的实现应考虑对象中用于确定等价性的所有组件,并计算出一个整数值。- Java提供了
Objects.hash()
工具方法来简化涉及多个字段的hashCode() 的实现。
-
equals() 的错误用法:
- 错误地重载equals() 而非重写,会导致编译时类型检查失败,从而调用错误的方法实现。
-
equals() 的一致性原则:
- 必须保证equals() 实现是一个等价关系,否则依赖于等价性的操作(如集合搜索)将表现出不可预测的行为。
4. Object合同中的equals()
- 合同要求:
- 自反性:每个对象都应等于自己。
- 对称性:如果a.equals(b),则b.equals(a)。
- 传递性:如果a.equals(b)且b.equals(c),则a.equals(c)。
- 一致性:除非对象信息被修改,否则多次调用应返回相同结果。
- 对于非空引用x,x.equals(null)应返回false。
- 相等的对象必须有相同的hashCode。
5. hashCode() 合同
- 要求:
- 多次调用hashCode()应返回相同整数值,前提是对象未被修改。
- 相等的对象必须有相同的hashCode值。
- 不要求不相等的对象有不同hashCode值,但这样可能影响哈希表性能。
6. 可变类型与不可变类型的等价性
- 不可变类型:
- equals()应比较抽象值,即行为等价。
- hashCode()应将抽象值映射为整数。
- 必须重写equals()和hashCode()。
- 可变类型:
- equals()应比较引用,即行为等价。
- hashCode()应将引用映射为整数。
- 不应重写equals()和hashCode(),使用Object的默认实现。
7. Autoboxing与等价性
- 概念:基本类型和它们的包装类(如int和Integer)之间的自动转换。
- 问题:Autoboxing可能导致意料之外的等价性判断。
8. 总结
- 等价性是实现ADT的一部分,应为等价关系。
- 等价性和hashCode必须一致,以确保如HashSet和HashMap这类使用哈希表的数据结构正常工作。
- 抽象函数是不可变类型等价性的基础。
- 引用等价性是可变类型等价性的基石,这是确保一致性并避免破坏哈希表RI的唯一方法。