在集合论与图论中我们曾经学过,有关等价的定义:应满足自反性,传递性与对称性。
那么对于ADT来说等价性的定义是什么呢:
在软件构造这一课程中,我们学习了两种判定方法,分别是AF方式判定,观察者方式判定
AF方式判定:对于之前博客中提到的的RI中的元素,经过AF映射后得到相同的结果则可判断为等价
观察者方式判定:站在外部观察者的角度,如果两个搞对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的
如下图题:
在忽略大小写和除去非字符字符串的情况下,找出包含字母相同的集合,在如此AF的要求下,1,2,5号中都包含了a(A),b(B),c(C)
在size()的作用是返回的是集合中不同元素的个数,contains()的作用是通过调用equals()来判断集合中包含哪些元素,length()返回字符串长度,union()是并操作,isAllLower()是判断字母大小写,first()是返回首字母。
contains()单独时对于AF中描述的等价的字符串做出操作后都能得到相同的结果,size()的问题是在不等价的字符串中可能返回相同的结果,如“AC”,“AB”,都会返回2;第三个是可以做到判断等价性的;第四个是length对于AF中要求的等价性相同的字符串会返回不同的值,如“AAA”和“A”
“==”和equals:
==判断内存地址是否相等;equals是对象等价性
在定义ADT时,需要根据对“等价”的要求,决定是否重写equals()
Object中实现的equals通常不是我们程序所需要的,因此需要进行重写
而在重写时,需要签名与Object中的equals()完全一致,否则会被视为重载,在程序内部对于equals()的调用时发生调用的错误导致程序结果的错误,需要设置@Override来进行签名的检查。
判断下图中最终的打印结果:
最终会打印false;
因为Name类中的equals()的参数类型为Name而非Object,所以编译器会认为这是一个重载,而contains在进行调用时进行判断的依据是调用equals(),而这里面的equals()会进行地址空间的检查,而虽然判断的内容与add内容一致,但是由于是new会新申请一段地址空间,所以会返回false。
下图的打印结果?
true,由于equals()被重写,equals()判断正常
一个正确的equals():由于没有AF,所以并不规范
intanceof作为一个操作符,用来判断o是否是PhoneNumber产生的对象,其功能与方法getClass()类似
哈希值,hashCode():
作为一个在数据结构中接触过的概念,java中只有涉及到hashSet等特殊结构时才会牵扯hashCode,平时并不会进行使用。
同一程序多次调用一个对象的hashCode()方法时,应返回相同的值,因为一次执行中,哈希值应该是统一不变的。
但多次执行时,同一对象不需要相同的哈希值。
不同的对象也可以拥有相同的哈希值,但这样做会降低程序性能。
如果仅仅为了简单方便操作考虑,可以给所有对象相同的哈希值,但这样会使程序性能大大降低。
对于可变的数据类型:
对于可变的数据类型来说,等价性的判断通常会变得困难,但也存在一系列标准。
观察等价性:在不改变的状态下,两个可变对象看起来是否一致
行为等价性:调用对象的任何方法都展示出一致结果
对于可变类型,往往倾向于更严格的观察等价性,但有些时候,观察等价性可能产生bug,甚至破坏RI,如对象的哈希值改变后,hashSet不能及时更新哈希桶的位置,导致哈希值找不到对对应元素,目前,这方面的有些问题,一直不能得到有效的解决。
下图题对于可变类型的判断进行了简单的测试,由于比较简单,不进行详细解释