软件构造随笔二

在最近的学习中,课程讲到了关于重写equals方法的内容。这方面的内容介绍了何为Java ADT的相等,我觉得这部分内容很有用,因为在我的实际编程中确实有很多场合需要重写qeuals方法或比较两个ADT。在阅读MIT对应的阅读材料15之后,我想在这篇博客分享一下我的收获。

首先在开头要感谢一下李秋豪学长的翻译版本,中英对照着看能让我理解的更好。

在阅读材料中介绍了三种相等的定义,前两种都属于抽象函数的范畴,我们很好理解。但在第三种相等的判定中,我们是通过用户的视角来判断ADT的相等,从ADT来说,“观察”就意味着使用它的观察者/操作。所以我们也可以说两个对象相等当且仅当它们的所有观察操作都返回相同的结果,这也是我们在一些情况下想达到的相等,如:
软件构造随笔二
我们定义两个Ex类相同是其成员变量都相同,我们就可以用get方法观察,看其是否相等。

该部分内容中介绍了两种比较相等的方式:“==” 和 equals方法,在mit的阅读材料中有如下的定义:

The == operator compares references. More precisely, it tests referential equality. Two references are== if they point to the same storage in memory. In terms of the snapshot diagrams we’ve been drawing, two references are == if their arrows point to the same object bubble.

The equals() operation compares object contents – in other words, object equality, in the sense that we’ve been talking about in this reading. The equals operation has to be defined appropriately for every abstract data type

即==是测试的是指向相等(referential equality),而equals测试的是对象值相等(object equality)

我们一般意义上的相等应该用Equals判断。

而equals方法在定义是执行相同:
软件构造随笔二
所以,重写equals方法总是必要的。像我们比较字符串相等时常用的equals方法就是重写后的。

3.重写equals易犯的错误

第一个错误很容易被忽略,在课堂上老师也用这个举过例子。

软件构造随笔二
软件构造随笔二
容易犯错的地方是d1.equals(o2)的结果,出错的原因就是在重写equals方法时没有加@override,这样的话只算方法的重载。

就像我们使用的println方法一样,重载的方法根据参数来选择具体调用哪一个方法。

所以出现了上述的错误。

还有让我印象比较深的就是破坏哈希值的例子。

软件构造随笔二

在此之前,我对泛型如List,Set操作的印象是不能在遍历的时候改变,如使用for循环遍历List是使用add方法就会抛出线程异常,而上图的错误是我第一次见。

而发生错误的原因就是因为 List 是一个可变对象,而在Java对可变对象的实现中,改造操作通常都会影响 equals() 和 hashCode()的结果。所以列表第一次放入 HashSet的时候,list是存储在这时 hashCode() 对应的索引位置。但是后来列表发生了改变,计算 hashCode() 会得到不一样的结果,但是 HashSet 对此并不知道,所以我们调用contains时候就会找不到列表。
当 equals() 和 hashCode() 被改动影响的时候,我们就破坏了哈希表利用对象作为键的不变量。
这一点一定要注意。

以上就是我目前位置认为在这些内容中需要注意的部分,以后如果对这些内容有了新的认识,我会陆续添加到该博客。上述内容如有错误,欢迎指出。