两个索引在不改变各自对象状态的前提下不能被区分。即通过只调用observer,producer和creator的方法,它测试的是这两个索引在当前程序状态下“看起来”相等。
行为等价性两个索引在任何代码的情况下都不能被区分,即使有一个对象调用了改造者。它测试的是两个对象是否会在未来所有的状态下“行为”相等。
对于不可变对象,观察相等和行为相等是完全等价的,因为它们没有改造者改变对象内部的状态。
对于可变对象,Java通常实现的是观察相等。
java提供了两种比较两个对象是否相等的方法
“==”比较的是引用等价性,即比较的两个对象是否指向内存中的同一段空间,基本数据类型的比较可直接使用 该种方法。
equals在未重写是也是比较引用等价性,这是因为在Object中实现的equals方法是通过“==”判断的,如下图:
图1:Object类中的equals()方法判断等价性的方式
显然,这种方式不能满足程序员对比较两个对象等价性的需求。为了使equals更适应实际使用的需求,我们在设计ADT时需要重新该方法。我们可以通过AF来定义等价性,如果两个对象通过AF抽象函数映射到同一个对象,我们认为这两个对象等价,如1分02秒和62秒就可视为等价。我们也可以站在观察者的角度来定义等价性,如果对两个对象执行相同的操作会得到相同的结果,不能将它们区分开来,则这两个对象视为等价。如对“ab”,“ba”执行length(返回字符串长度)、是否包含a、是否包含b等操作,两者都会得到相同的结果。
重写时要注意传入equals的参数的类型,避免误写为重载。添加@override,编译器可以提示我们这种错误。
下面是一个重写equals方法的例子,第一步先进行类型比较和null值判断,若类型不符或者传入参数为null值,则直接返回false,接下来再进行正常的比较,若根据AF来定义等价性,则按照AF来实现该方法,这里不妨假设F中lineNumber、prefix、areaCode分别相等的两个对象映射到同一个A中的同一个对象。(下图中没给出AF是不严谨的)
图2:在PhoneNumber类中重写equals()方法
最后实现的equals方法应是自反、对称、可传递的
重写hashCode()方法在重写equals方法后需要重写hashCode()方法,除非你能保证设计的ADT不会被放入到Hash类型的集合类中。hashCode()方法返回一个int的哈希码两个相等的对象需要有同样的hashcode。
若两个相等对象的hashcode不同,则将其添加进Hash类型的集合类中时,无法通过与之相等的对象来查找。如下面这段代码:
public class TestHashMap {
public static void main(String[] args) {
HashMapKey k1 = new HashMapKey(1);
HashMapKey k2 = new HashMapKey(1);
HashMap map = new HashMap<>();
map.put(k1, "stringk1");
System.out.println("map.get(k2) : " + map.get(k2));
}
}
在未重写hashCode方法时无法通过与k1相等的k2来寻找对应的value。当我们往 HashMap 里放 k1 时,首先会调用 HashMapKey 这个类的 hashCode 方法计算它的 hash 值,随后把 k1 放入 hash 值所指引的内存位置。但是我们没有在 HashMapKey 里重写 hashCode 方法,所以这里调用的是 Object 类的 hashCode 方法,而 Object 类的 hashCode 方法返回的 hash 值其实是 k1 对象的内存地址(假设是 0x100)。
如果我们随后是调用 map.get(k1),那么我们会再次调用 hashCode 方法(还是返回 k1 的地址 0x100),随后根据得到的 hash 值,能很快地找到 k1。
但我们这里的代码是 map.get(k2),当我们调用Object类的 hashCode方法(因为 HashMapKey 里没定义)计算 k2 的 hash值时,其实得到的是 k2 的内存地址(假设是 0x200)。由于 k1 和 k2 是两个不同的对象,所以它们的内存地址一定不会相同,也就是说它们的 hash 值一定不同,这就是我们无法用 k2 的 hash 值去拿 k1 的原因。
此外,用get(key)方法,计算出hash值后,会用equals方法去相应的槽寻找,故只有重写这两个方法才能得到正确的结果。
重写hashcode()时可调用用来做等价性判断的对象的hashcode(方法),因为他们的值必定相同,满足要求。如下:
return lastName.toUpperCase().hashCode();
eclipse也提供了快速重写hashCode()方法的功能,能熟练手动编写后可直接利用该方式快速完成。
图3:eclipse中重写hashCode()方法的快捷方式



