软件构造复习——ADT和OOP中的等价性(PPT8)


//主要是观察等价性和行为等价性,一个是从客户去看等价就行,一个是一个对象有两个引用,这两个引用行为等价。


在这里插入图片描述

一、等价关系

等价关系是指:自反、对称和传递
考试常考大部分出错在传递上
在这里插入图片描述

二、不可变类型的相等性

从函数的角度去看其相等性
有相同的结果则等价!

站在外部观察者角度:对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。

反之亦然
在这里插入图片描述
例子:
在这里插入图片描述
问你可以添加下面哪个方法,一定要注意提供的操作不应该违反AF!!
在这里插入图片描述

三、对于Mutable类型的==和equals()

在这里插入图片描述

在这里插入图片描述

对于M
==是引用等价性(指向同一个内存空间)
equals是对象等价性(内容相等,有时需要对其进行重写)

四、实施equals()方法

在这里插入图片描述
在这里插入图片描述
(静态类型检查)
类 Duration 重载了 equals() 方法,因为方法签名与 Object 的不同。
我们实际上在 Duration 中有两个 equals() 方法:
– 从 Object 继承的隐式 equals(Object)
– 新的 equals(Duration) 。
Java 编译器使用参数的编译时类型在重载操作之间进行选择。 静态类型检查。

所以写equals方法的时候,我们不能改变输入的参数!!!
在这里插入图片描述

扫描二维码关注公众号,回复: 13398777 查看本文章

@override可以帮我们检查这个错误
很容易在方法签名中犯错误,并在您打算覆盖它时重载方法。
§ Java 的注解 @Override 应该在您打算覆盖超类中的方法时使用。
§ 有了这个注解,Java 编译器会检查超类中是否确实存在一个具有相同签名的方法,如果你在签名中犯了错误,就会给你一个编译器错误

在这里插入图片描述
instanceof 运算符测试对象是否是特定类型的实例。
§ 使用 instanceof 是动态类型检查,而不是静态类型检查。
§ 一般来说,在面向对象编程中使用 instanceof (所有父类)是一种不好的味道。 除了实现 equals 之外,在任何地方都应该禁止它。
§ 此禁令还包括检查对象运行时类型的其他方式。
– 例如,getClass() (当前类)也是不允许的

在这里插入图片描述
在这里插入图片描述

五、The Object contract

在这里插入图片描述
等价关系:自反、传递、对称
除非对象被修改了,否则调用多次equals应同样的结果
“相等”的对象,其hashCode()的结果必须一致

用“是否为等价关系”检验你的equals()是否正确():
自反——每个对象都等于它自己
§ 对称——如果 a.equals(b) 那么 b.equals(a)
§ 可传递的——如果 a.equals(b) 和 b.equals©,那么 a.equals©
§ 一致——除非发生变异,否则相等的对象保持相等
§ “非空”——a.equals(null) 返回 false
§ 综上所述确保 equals 是对所有对象的全局等价关系
§用“是否为等价关系”检验你的equals()是否正确

我们必须确保 equals() 实现的等价定义实际上是之前定义的等价关系:
自反的、对称的和传递的。
– 如果不是,则依赖于相等性的操作(如集合、搜索)将表现得不稳定且不可预测。
– 您不想使用有时 a 等于 b 但 b 不等于 a 的数据类型进行编程。
– 会产生微妙而痛苦的错误。
在这里插入图片描述
哈希表是映射的表示:一种将键映射到值的抽象数据类型。
– 哈希表提供恒定时间查找,因此它们的性能往往比树或列表更好。 除了提供 equals 和 hashCode 外,键不必排序,也不必具有任何特定属性。
§ 哈希表的工作原理:
– 它包含一个数组,该数组被初始化为与我们期望插入的元素数量相对应的大小。
– 当一个键和一个值用于插入时,我们计算键的哈希码,并将其转换为数组范围内的索引(例如,通过模除法)。 然后在该索引处插入该值。
§ 哈希表的表示不变量包括键位于由其哈希码确定的槽中的基本约束
在这里插入图片描述
相同的对象,hashcode一定一样,不同的对象,hashcode可以一样可以不一样

重写hashcode
在这里插入图片描述
在这里插入图片描述
举例
在这里插入图片描述
在这里插入图片描述
一定要根据equals的具体例子去写,不可以想当然去重写hashcode

六、可变类型的等价性

在这里插入图片描述
对于可变对象,分观察等价性和行为等价性。
观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致
行为等价性:调用对象的任何方法都展示出一致的结果

注意:对于不可变对象,观察相等和行为相等是相同的,因为没有任何 mutator 方法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
List 是一个可变对象。 在标准 Java 中
List 等集合类的实现,突变会影响 equals() 和 hashCode() 的结果。
§ 列表第一次放入HashSet时,会存储在其当时hashCode()结果对应的hash桶中。
§ 当列表随后发生变异时,它的 hashCode() 会发生变化,但 HashSet 没有意识到它应该移动到不同的存储桶。 所以再也找不到了。
§ 当 equals() 和 hashCode() 可能受变异影响时,我们可以打破使用该对象作为键的哈希表的表示不变量

就好像:你在派出所申领了身份证,留了当时的照片;
几个月以后,你“整容”了(mutated),你坐飞机案件的时候就无法匹
配到你的身份证照片了…

在这里插入图片描述
在这里插入图片描述

如果将可变对象用作集合元素,则必须非常小心。
§ t. 如果某个mutable的对象包含在Set集合类中,当其发生改变后,集合类的行为不确定 。务必小心!
§ 不幸的是,Java 库对可变类的 equals() 的解释不一致。
§ 集合使用观察相等,但其他可变类(如 StringBuilder)使用行为相等。
在这里插入图片描述
行为等价性
在这里插入图片描述

equals() 和 hashCode() 的最终规则
§ 对于不可变类型:– equals() 应该比较抽象值。 这与说 equals() 应该提供行为平等相同。
– hashCode() 应该将抽象值映射到整数。
– 所以不可变类型必须覆盖 equals() 和 hashCode() 。
§ 对于可变类型:
– equals() 应该比较引用,就像 == 一样。 同样,这与说 equals() 应该提供行为平等相同。
– hashCode() 应该将引用映射为整数。
– 所以可变类型根本不应该覆盖 equals() 和 hashCode(),而应该简单地使用 Object 提供的默认实现。 不幸的是,Java 的集合没有遵循这个规则,这导致了我们上面看到的陷阱

在这里插入图片描述

对象中的 clone()
§ clone() 创建并返回此对象的副本。
§ “复制”的确切含义可能取决于对象的类别。
§ 一般意图是,对于任何对象 x
在这里插入图片描述
在这里插入图片描述

七、自动装箱和等价性

原始类型及其对象类型等价物,例如 int 和 Integer 。
§ 如果您创建两个具有相同值的 Integer 对象,它们将彼此相等。
§ 但是如果 x==y 呢? ----- False(仅仅值相等,引用不等)
§ 但是如果 (int) x == (int) y 呢? - - True
§ 这段代码的结果是什么
在这里插入图片描述
在这里插入图片描述
-128-127会自动拆箱,Interger -> Int,这样 == 就可以为true了,指向同一个对象了

总结:
相等是实现抽象数据类型 (ADT) 的一部分。
– 平等应该是一个等价关系(自反、对称、传递)。
– 等式和哈希码必须相互一致,这样使用哈希表的数据结构(如 HashSet 和 HashMap )才能正常工作。
– 抽象函数是不可变数据类型相等的基础。
– 引用相等是可变数据类型相等的基础; 这是确保随时间推移的一致性并避免破坏哈希表的表示不变量的唯一方法。

猜你喜欢

转载自blog.csdn.net/m0_50906780/article/details/118404071
今日推荐