ADT和OOP中的“等价性”

文章探讨了对象等价性的不同方面,包括不可变类型的等价性基于抽象函数,`==`与`.equals()`的区别,以及观察等价性和行为等价性在可变和不可变对象中的应用。强调了在自定义ADT时重写`.equals()`和`.hashCode()`的重要性,以及可变类型中应考虑的行为等价性。对于可变对象,文章建议提供额外的`similar()`方法来判断内容相等,而不是依赖`.equals()`。
摘要由CSDN通过智能技术生成

1.不可变类型的等价性

        可以用AF来判断等价性。抽象函数是将R→A将数据类型的具体实例映射到相应的抽象值。如果AF映射到同样的结果,则等价。等价关系引出一个抽象函数(关系划分T,因此f将每个元素映射到它的划分类)。抽象函数引出的关系是等价关系。

        站在外部观察者角度:对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。当两个物体不能通过观察加以区分时,我们可以说它们是相等的——我们可以应用的每个操作对两个物体产生相同的结果。考虑集合表达式{1,2}和{2,1}。使用对集合、基数性|…|和隶属度∈可用的观察者操作,这些表达式是不可区分的:

 在ADT中,“观察”意味着调用对象上的操作。因此,当且仅当不能通过调用抽象数据类型的任何操作来区分两个对象时,它们是相等的。

2.== 和. equals()

        ==表示的是引用等价性,即是否是同一对象、同一地址。==操作符比较引用。它测试参照相等性。如果它们指向内存中的相同存储就使用==。就快照图而言,如果两个引用的箭头指向相同的对象泡,则它们是==。equals()操作比较对象内容——换句话说,对象内容相等。所以在自定义ADT时,需要重写Object的equals()。

        对基本数据类型,使用==判定相等,对对象类型,使用equals()。如果用==,是在判断两个对象是否指向内存里的同一段空间。大部分情况下想要判断的还是内容相等,故应该经常使用equals(使用前重写)。

        重写equals不一定要比较所有对象拥有的属性。严格来说,在没有 AF的情况下直接在equals()中判断每个属性的等价性,是不正确的。所以大部分情况下是需要根据AF来重写equals的。此外,比较属性前应用instanceof来判断比较对象是否是该类型。

        重写equals往往伴随着hashcode的变化,所以总是要在重写equals时重写hashcode()。保证equals相等时hashcode也相等即可,而hashcode相等时并不意味着equals比较相等,所以会存在哈希冲突。在重写hashcode时,我们应该有意识的避免哈希冲突,用一些单数或质数来相加会更好。

 

3.观察等价性和行为等价性

        观察等价性是在不改变状态的情况下,两个mutable对象是否看起来一致。当它们不能通过不改变对象状态的观察来区分时,即,通过只调用观察器、生产器和构造器方法。这通常严格地称为观察性相等,因为它测试两个对象在程序的当前状态下是否“看起来”相同。而行为等价性是调用对象的任何方法都展示出一致的结果。当它们无法通过任何观察加以区分时,甚至状态也会发生变化。这种解释允许调用两个对象上的任何方法,包括mutator。这被称为行为平等,因为它测试两个对象在当前和未来的所有状态下是否会“表现”相同。对于不可变对象,观察相等和行为相等是相同的,因为没有任何mutator方法(在表示不会泄露的前提下)。

        但是对可变类型来说,往往也倾向于实现严格的观察等价性。Java对大多数可变数据类型(如
集合),但其他可变类(如StringBuilder)使用行为相等。如果两个不同的List对象包含相同的元素序列,则equals()报告它们相等。但在有些时候,观察等价性可能导致bug,甚至可能破坏RI。

例如:

        这个例子举得非常巧妙,在上述情况下,set中的list在外部被直接改变了,但set的哈希桶是在set一开始建立的时候就被分配好了,即使再改变其内容也不会改变已有元素的哈希桶分配,所以当list改变时,他的hashcode明明已经改变了,但是因为set没有改变哈希桶,导致无论如何搜索都不能在set中找到list了。其实有一种解决办法就是new一个set把当前的set复制过去,但是这样每次list改变的时候都要重新分配,复杂度过高,显然不是我们想要的。

        所以如果某个mutable的对象包含在 HashSet集合类中,当其发生改变后,集合类的行为不确定。对可变类型,实现行为等价性即可,一般来说,这意味着当且仅当两个引用是同一对象的别名时,它们应该是equals()。所以对可变类型来说,无需重写hashcode和equals,直接继承Object的两个方法即可。若要判断内容相等的话,可提供一个新的方法similar()。

4.总结

        这块的难点在于行为和观察等价性,只需要记住可变类型equals默认不需要重写,而不可变类型就没有这个烦恼,直接实现观察等价性即可。希望能对你产生帮助,欢迎讨论和提出问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值