8 Equality in ADT and OOP ADT和OOP中的“等价性”

8 Equality in ADT and OOP ADT和OOP中的“等价性”

Outline

在很多的场景下,我们需要判断两个对象是否相等,在这个时候往往会使用到“==”和“.equals”方法,那么,接下来我们将具体地探究如何判断两个对象的相等关系。

1 Equivalence Relation

现实世界中每个对象都是独一无二的,无法存在完全的相等,但是在人类语言和数学中,绝对的相等是存在的。引用集合论中对于等价关系的定义:

An equivalence is a relation E ⊆ T x T that is:

– reflexive: E(t,t) ∀t∈T

– symmetric: E(t,u) ⇒ E(u,t)

– transitive: E(t,u) ∧ E(u,v) ⇒ E(t,v)

– To use E as a definition for equality, we would say that a equals b if and only if E(a,b).

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

2 Three ways to regard equality

2.1AF(Abstract Function)

ADT是数据的抽象,体现为一组对数据的操;那么AF在其中充当了内部表示到抽象表示的桥梁,相当于是一种映射。那么站在外部观察者的角度,如果对对象调用任何的操作,都会得到两个相同的结果,则可以认为这两个对象是等价的。

这里需要学会判断在观察定义等价相同的意义上,哪些方法的选择是与AF一致的。

有些时候单独的一个方法不能准确的判断,需要多个方法组合判断。

2.2“==”引用等价性

利用“==”来测试的是引用的等价性,只有两个对象在内存中的地址是相同的才会被认定为相等。换句话来说,这个时候查看snapshot(快照图),两个相等的引用的指针指向同一个对象。

对于基本数据类型,一般使用“==”判定相等。

2.3“.equals( )”对象等价性

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

对于对象而言,一般来说会使用.equals()方法来进行判定。是否重写.equals()方法,取决于其内在的判定逻辑。如果判定的逻辑就是内存地址相等,就不用重写;如果判定的逻辑是一种特定的,这个时候就需要根据这种特定的逻辑来重写。对于对象而言,无论是哪种情况使用equals()方法都会达到目标的预期。

3 Implementing equals()

在缺省的情况下,默认判断引用等价性,不过这通常不是程序员所希望看到的:

在这里插入图片描述

要注意我们要做的事情是override而不是overload,因此引用的是父类object而不是子类

比如下面这种情况就是错误的:

在这里插入图片描述

那么如果这样做会发生什么呢?

Duration类重载了equals() 方法,因为方法签名与Object的不同。我们实际上在Duration中有两个equals() 方法:

– 从 Object 继承的隐式 equals(Object)

– 新的 equals(Duration)。

Java 编译器使用参数的编译时类型在重载操作之间进行选择。(静态检查)

如果我们传递一个Object 引用,就像d1.equals(o2) 中一样,我们最终会调用equals(Object) 实现。如果我们传递一个Duration 引用,如在d1.equals(d2) 中,我们最终会调用 equals(Duration) 版本。即使 o2 和 d2 在运行时都指向同一个对象,也会发生这种情况! 这样会使相等难以判断,在编译阶段已经去定了调用的方法不同。

对于初学者而言非常容易犯这样的错误,错误地使用了标签,因此当覆盖超类的方法,都应该使用Java的注释@Override,这样Java编译器将检查超类中是否确实存在相同签名的方法,如果在签名中犯了错误,编译器会返回一个错误。
在这里插入图片描述

在重写中有几个技巧,比如使用instanceof()来判断某个对象时不时某个特定的类型或其子类型,注意是动态类型检查。

4The Object contract

对象的Equals方法有几个约定:

  1. 必须满足等价关系:即自反、对称、传递。

  2. 除非对象被修改了,否则多次调用的结果是一致的。

  3. 相等的对象的hashcode必须是相等的。

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

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

    哈希表是如何工作的:

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

    – 当一个键和一个值出现插入时,我们计算键的哈希码,并将其转换为数组范围内的索引(例如,通过模除法)。 然后将该值插入该索引处。

    哈希表的表示不变量由其哈希码确定的槽中的基本约束。 Hashtable的RI中的基本要求就是key在slot中的位置由hashcode确定。
    在这里插入图片描述

在程序中调用相同的对象必须返回相同的hashcode,不同的对象也可以映射为相同的hashcode,但是性能会变差。比较合理的做法是,通过计算equals用到的油油的信息的hashcode组合出新的hashcode。当然你也可以考虑无需改写hashcode,但是你要保证你的ADT永远都不会被放到Hash类型的集合类。

5Equality of Mutable Types

对于可变类型的对象,需要注意观察等价性和行为等价性。Java对其大多数的可变数据类型使用观察等价性,部分使用行为等价性。在有些时候观察等价性可能导致bug,甚至可能破坏RI。

如果某个mutable的对象包含在Set的集合类中,当其改变后,集合类的行为不确定,在JDK中,不同的mutable类使用不同的等价性标准。

对于可变类型,实现行为等价性即可,只有志向相同内存空间的的object才是相等的。对于可变类型的,无需重写两个函数,直接继承object的两个方法即可,如果一定要判断两个可变对象看起来是否一致,最好定义一个新的方法。

另外可以使用IDEA自带的功能自动生成上述代码。
bject才是相等的。对于可变类型的,无需重写两个函数,直接继承object的两个方法即可,如果一定要判断两个可变对象看起来是否一致,最好定义一个新的方法。

另外可以使用IDEA自带的功能自动生成上述代码。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HIT-Steven

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值