软件构造课笔记:第8章——ADT和OOP中的“等价性”

ADT和OOP中的“等价性”

等价关系

现实中的每个对象实体都是独特的,所以无法完全相等,但有“相似性”,在人类语言和数学中,“绝对相等”是存在的

等价关系:自反、对称、传递

重视平等的三种方式

1.ADT上的相等运算

ADT是对数据的抽象, 体现为一组对数据的操作

对于抽象数据类型,抽象函数(AF)解释了如何将具体表示值解释为抽象类型的值,我们看到了抽象函数的选择如何决定如何编写实现ADT的每个操作,抽象函数AF:内部表示→抽象表示

基于抽象函数AF定义ADT的等价操作

2.使用AF定义相等

抽象函数AF:R→A将数据类型的具体实例映射到它们对应的抽象值。

使用AF作为等式的定义,我们说a等于b当且仅当AF(a)=AF(b)映射到同样的结果,则等价

使用AF和使用关系来定义相等是等效的。

等价关系引发了一个抽象函数(该关系划分T,因此AF将每个元素映射到其划分类)。

由抽象函数引发的关系是等价关系。

3.用观察来定义等式

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

相等的不同表示:==与equals()

==运算符比较引用。它测试引用相等性。如果两个引用指向内存中的相同存储,则它们为==。就快照图而言,如果两个引用的箭头指向同一个对象气泡,则它们为==。引用等价性

equals()操作比较对象内容。对象等价性

在自定义 Object 的 ADT 时,需要根据对“等价”的要求,决定是否重写 equals()

== 对基本数据类型,使用==判定相等;对对象类型,使用equals()

实现equals()

在Object 中实现的缺省 equals() 是在判断引用等价性,不是程序员所希望的,需要重写

Java编译器使用参数的编译时类型。静态类型检查

instanceof

判断某个对象是不是特定类型(或其子类型)

动态类型检查,不是静态类型检查

除了用于实现 equals() 方法,尽可能避免使用 instanceof 和 getClass () !

对象之间的合约

对象中equals()中的合约

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

打破等价合约

我们必须确保equals()实现的等式定义实际上是前面定义的等价关系:自反、对称和传递。

如果不是,那么依赖于相等的操作(如集合、搜索)将表现得不稳定和不可预测。

破坏哈希表

哈希表是映射的表示:一种将键映射到值的抽象数据类型。

哈希表提供了恒定的时间查找,因此它们往往比树或列表执行得更好。除了提供equals和hashCode之外,密钥不必排序,也不必具有任何特定属性。

哈希表的工作方式:

-它包含一个数组,该数组被初始化为与我们期望插入的元素数量相对应的大小

-当一个键和一个值被提供用于插入时,我们计算该键的哈希码,并将其转换为数组范围内的索引(例如,通过模除法)。然后将该值插入该索引决定了数据被存 储到数组的哪个位置

哈希表的rep不变量包括密钥位于由其哈希代码确定的槽中的基本约束。

哈希表中基本要求就是密钥槽散列码

hashCode合约

在应用程序的执行过程中,每当对同一对象多次调用hashCode方法时,只要不修改对象上的equals比较中使用的信息,hashCode方法就必须始终返回相同的整数。程序中多次调用同一对象的 hashCode方法,都要返回相同值

从一个应用程序的一次执行到同一应用程序的另一次执行,该整数不需要保持一致。不要求程序的多次执行时相同

如果根据equals(Object)方法,两个对象相等,那么对这两个对象中的每一个调用hashCode方法必须产生相同的整数结果。等价的对象必须有相同的hashCode

然而,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。不相等的对象,也可以映射为同样的hashCode

1.相等的对象必须具有相等的哈希代码:如果覆盖equals,则必须覆盖hashCode
2.不相等的对象应具有不同的哈希代码:构造时考虑所有值字段
3.除非对象发生突变,否则哈希代码不得更改

破坏哈希表

为什么Object合约要求相等的对象具有相同的哈希代码?

-如果两个相等的对象具有不同的哈希代码,则它们可能被放置在不同的槽中。

-因此,如果您试图使用与插入值的键相同的键来查找值,则查找可能会失败。

重写hashCode

最简单方法:让所有对象的hashCode为同一常量,符合contract,但降低了hashTable效率

通过 equals计算中用到的所有信息的hashCode组合出新的hashCode

两个equal的objects,一定要 有同样的hashcode.

重写equals()时始终重写hashCode()
除非你能保证你的ADT哈希类型的集合类中

可变类型的相等性

观察等价性: 在不改变状态的情况下, 两个 mutable 对象是否看起来一致

行为等价性:调用对象的任何方法都展示出一致的结果

对可变类型来说,往往倾向于实现严格的 观察等价性

Collections Java 对其大部分可变数据类型(如 )使用观察等价性,如两个 则 equals() 返回 true List 中包含相同顺序的元素, 。但是部分可变类型用行为等价性。

但在有些时候,观察等价性可能导致 bug ,甚至可能破坏 RI

equals()和hashCode()的最终规则

对于不可变类型:
equals()应该比较抽象值。这与equals()应该提供行为平等是一样的。
hashCode()应该将抽象值映射到一个整数。
因此,不可变类型必须覆盖equals()和hashCode()。

对于可变类型:
equals()应该比较引用,就像=一样。同样,这与equals()应该提供行为平等是一样的。hashCode()应该将引用映射为一个整数。
因此,可变类型根本不应该覆盖equals()和hashCode(),而应该只使用Object提供的默认实现。不幸的是,Java在其集合中没有遵循这一规则,这导致了我们在上面看到的陷阱。

自动包装与等价

基元类型及其对象类型等价物,例如int和整数
如果创建两个具有相同值的Integer对象,它们将是彼此相等()

  • 22
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值