ADT和OOP中的等价性

在软件构造中,常常需要判断两个对象是否是等价的。在Java中,由于存在两种数据类型Immutable和Mutable,其对于等价的判断也是不相同的。

什么是等价关系呢?等价关系应该满足自反、对称、传递的特性。

有两种定义等价性的角度。
一种是从抽象函数AF的视角,AF映射到同样的结果,则等价;
另一种是站在观察者角度,对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。                                                                                                                                                                                                                                                                                            对可变类型的等价性和不可变类型的等价性分别讨论会更加清晰。                                                   
一、Immutable类型的等价性
在代码中,判断两个对象是否具有等价关系,有两种方式,"=="和.equals()方法。两者分别实现了不同的判定方式。

"=="表达引用等价性
用"=="来判断等价性,实际上考量的是两个对象在内存中的地址空间是否一致,对于Immutable类型的对象来说这又被称为引用等价性。一般情况下,对于基本数据类型的判断如int, long, double等都会使用"=="来判断引用等价性。

如果两个对象是引用等价的,那么这两个对象其实就是一个对象,那么他就会满足所有的等价性。

.equals()表达对象等价性
用".equals()"方法来判断等价性,要求就没有"=="那么高了,也就是说,满足等价的定义即可。两个对象不一定要是同一个对象,只要这两个对象在我们所关心的维度是等价的就行了,所以可以看出,用equals()方法来判断对象的等价关系是基于实际情况的可以个性化实现的。一般情况下,对于对象类型的数据都会使用equals()方法来判断等价性。

那么如何基于实际情况个性化实现呢?equals()方法是继承自Object()这一父类的,而在Object()中equals()方法是实现引用等价性的,这常常不是程序员们所需要的。于是在写一个继承自Object()父类的ADT时,要对equals()方法进行重写。

重写equals()方法
我们首先要重写equals()方法,一定要保证重写后的equals()方法判断的等价性满足定义以及自反、传递、对称的特性。

但是重写了equals()方法就够吗?其实是不够的,我们还要重写Object()父类中的另一个方法HashCode()方法,这是因为在Object()父类中规定了如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 HashCode() 方法都必须生成相同的整数结果。基于这一原因,我们就必须要重写HashCode()保证equals()判断的等价的两个对象,他们的HashCode也要一致才行。如果两个对象不相等,则对这两个对象中的每一个调用hashCode方法都必须产生不同的整数结果。然而,不相等的对象,也可能映射为同样的hashCode,但这样的概率是很低的

如何重写hashCode()方法,最简单方法:让所有对象的hashCode为同一常量,符合约定,但降低了hashTable效率。最通用的方法时通过equals计算中用到的所有信息的hashCode组合出新的hashCode。一般通过IDE自动生成。

二、mutable类型的等价性

观察等价性:两个索引在不改变各自对象状态的前提下不能被区分。即通过只调用observer,producer和creator的方法,它测试的是这两个索引在当前程序状态下“看起来”相等。

行为等价性:两个索引在任何代码的情况下都不能被区分,即使有一个对象调用了改造者。它测试的是两个对象是否会在未来所有的状态下“行为”相等。

注意:

对可变类型来说,往往倾向于实现严格的观察等价性。Java对其大部分可变数据类型(如Collections)使用观察等价性,如两个List中包含相同顺序的元素,则equals()返回true。部分可变类型用行为等价性。但在有些时候,观察等价性可能导致bug,甚至可能破坏RI

例如:Set<List<String>>这样的结构,当List发生改变时,List的hashcode变了,但是存储List的HashSet没有更新其在bucket的位置,查找时在新hashcode的位置找不到元素。这就是equals和hashcode的结果被可变影响,导致RI被破坏。

因此,如果某个mutable的对象包含在Set集合类中,当其发生改变后,集合类的行为不确定,对于可变对象来说,实现行为等价性比较好

所以可变类型根本不应该重写equals()和hashCode()
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值