1. ADT是对数据的抽象,体现为一组对数据的操作
抽象函数AF:内部表示->外部表示
因此要基于抽象函数AF定义ADT的等价操作
2. ADT的等价性:
(1)现实中每个对象实体都是独特的
(2)所以无法完全相等,但有“相似性”
(3)而在数学中,绝对相等是存在的
3. 保证等价性的三条方式:
(1)通过AF:AF若映射到同样的结果,则等价
(2)通过关系:等价关系是自反,对称,传递的
(3)通过观察行为:站在外部观察者的角度,调用观察来判定其等价
4. == VS .equals():
(1)==是引用等价性,意为两个引用是否指向同一个对象,即同一个内存地址
(2)equals()是对象等价性,在自定义ADT时,需要对其进行重写,来保证判断其
等价的条件。
注: == 对基本数据类型,使用==判定相等 , 对对象类型,使用equals()
( 如果用==,是在判断两个对象 身份标识 ID是否相等(指向内存里的同一段空间) )
5. 重写equals()的方法:
参数一定为“Object”类型,因为这样是override(重写)
若为其他类型,则为overload(重载)
其具有区别:例如:若使用其他参数类型:
则在下列代码中:
明明o2和d2指向同一个对象,但由于引用类型的不同,而java在编译过程匹配Overload的
方法,d1.equals(o2)调用了未被覆盖的原Object的equals()方法,由于d1,o2指向内
存地址不同,返回为false。而使用重写方式,覆盖了原Object的equals()方法,
因此d1.equals(d2)返回为true。
正确的方式:
注意:1.先判断thatObject是否为Duration的类型,然后再将其转成Duration的引用,
再将转换后的引用对象调用方法和其进行比较。
6. 重写hashCode():
规定:(1)若一个对象equal另一个对象,则其hashCode必须(must)相同
(2)不同的对象,其hashCode应该(should)不同
(3)HashCode在对象mutate之前不能改变。
例:以电话号码类为例
hashCode的写法举例:
7. 可变类型的观察等价性与行为等价性:
观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致
行为等价性:调用对象的任何方法都展示出一致的结果
(1) 对可变类型来说,往往倾向于实现严格的观察等价性,
但在有些时候,观察等价性可能导致bug,甚至可能破坏RI
例:
这时,
然而:当之后:
更糟糕的是,在遍历set时,甚至会出现这种情况:
发生的原因:set使用了HashSet的实现方式,其元素的存储位置对应于放入时的
hashcode。
而list的hashcode和equals方法的判定是和其内容直接相关的。
当list内容变化后,其hashcode也变化了,而hashset没有意识到存在原来桶中的元素
的hashcode与桶不对应,因此hashset去新的hashcode对应的桶中寻找对象,结果没
有发现,因此出现了错误。
正如:
(2)因此,对可变类型:
a. 实现行为等价性即可,也就是说,只有指向同样内存空间的objects,才是相等的。
b.所以对可变类型来说,无需重写这两个函数,直接继承 Object对象的两个方法即
可。
c. 如果一定要判断两个可变 对象看起来是否一致,最好定义一个新的方法。