此为作者在学习软件构造课程lab2中遇到的问题。
由于在进行实验时,作者还并未学习equals(本校的第八讲:ADT和OOP中的等价性),故写出的代码有些许不严谨之处(注:lab2并未要求实现equals和hashcode,为本人为提高程序性能自行添加,求助教学长(姐)不要因为这些不严谨扣我的分数,呜呜呜)
问题在Java编程中,我们经常需要使用到Collection类及其子类,包括但不限于Set、Map、List等。
有时我们需要这些类同时处理两个成对的元素,并将成对的元素视作一个。比如在Map中我们可能希望一个value值需要两个key,key1和key2,两个key共同起到键值的作用,只有当key1和key2都相同时才能和value相对应。这种情况下如果能将两个key合并为一个,就能够直接使用已有的Collection,为编程带来了方便。
然而java标准库中并未提供Pair类,最简单的解决方法是用一个Map将两个元素值链接起来,这个Map中也只有这一个Entry,但是这样的话你的代码就会变成这个样子:
Map
然而这样做会带来诸多问题:
- Map内部的实现复杂,其equals和hashCode并不一定能实现我们想要的功能,这会带来及其难以调试的Bug。
- 这种写法极为繁琐,不仅难以阅读、编写,同时运行效率也较为底下。
那怎么办?可以考虑自行实现一个Pair类。
解决方案为了防止读者直接复制代码造成错误,特将说明写在前方
- Collection中有许多类用到了Hash table,简要的说它调用hashCode方法(Object类中会进行继承),将每一个对象转化为一个整形,之后利用这个整形对哈希表进行索引,索引之后利用equals方法从具有相同索引值的一系列对象中找到和你要的对象相等的那个。
- 对于可变类和不可变类,他们的相等的定义不同
- 不可变类的相等一般定义为AF的相等,或者定义为观察者方法的相等,这两种定义必须一致
- 可变类的相等可以定义为观察者的相等,也可以定义为内存地址的相等,然而使用观察者的相等作为equals会为哈希算法带来难以察觉的错误,详见参考资料Reading 15: Equality (mit.edu)。
为了解决这个问题,可变类型的equals一般约定定义为内存地址的相等 - 由于相等的定义的不同,为了达到我们想要的Pair在Collection中的表现,我们需要将Pair定义为一个不可变类
- 对于不可变类,一般约定需要重写继承自Object类的equals方法和hashCode方法。
出于以上原因,将Pair实现为不可变类型,并重写equals和hashCode:
public class Pair{ private T1 ele1; private T2 ele2; public Pair(T1 x,T2 y) { ele1=x; ele2=y; } @Override public boolean equals(Object anObject) { if(this==anObject) return true; if(anObject instanceof Pair,?>) { Pair,?> that=(Pair,?>)anObject; return this.ele1.equals(that.ele1)&&this.ele2.equals(that.ele2); } return false; } @Override public int hashCode() { return ele1.hashCode()*107+ele2.hashCode(); } }
其中hashCode可以自行按照想法进行编写,这里简单的将两个成员的hashCode用质数107组合起来
需要解释的是equals方法,这个方法要求重写Object中的方法,所以必须以object为参数
首先判断两个句柄是否相同,再用instanceof判断anObject是否是Pair类型,如果是的话进行下溯造型之后比较。
这里在确定anObject是一个Pair并下溯造型时,由于Java虚拟机在运行时并不保存泛型的类型信息,也就不知道这里的T1和T2具体是什么,需要使用Pair,?>来进行判断和下溯造型



