介绍一下Java中重写equals和hashCode方法的建议(资料整理自网络及书籍,在此表示感谢):
Java中对于对象等价的比较有两种方式,==比较的是两个对象的引用是否相同(即仅在两个对象指向同一块内存区域时才返回真),equals方法在Object类中定义,同样是比较两个对象的引用是否相等,但是在API中的类都会重写,对于自己定义的类通常情况下也需要我们根据抽象数据类型的性质重写。
对于判断等价性,有三种可供选择的方式:
1.利用AF。根据其abstract function,判断两个对象相等当且仅当其映射之后的抽象值相等。(感觉这里有坑,会再写一篇填坑)
2.利用关系。等价关系需要满足自反性,对称性和传递性,然后就可以据此划分为若干等价类。
3.利用观察。如果两个对象无法通过观察发现差异就认为它们是等价的。
1.利用AF。根据其abstract function,判断两个对象相等当且仅当其映射之后的抽象值相等。(感觉这里有坑,会再写一篇填坑)
2.利用关系。等价关系需要满足自反性,对称性和传递性,然后就可以据此划分为若干等价类。
3.利用观察。如果两个对象无法通过观察发现差异就认为它们是等价的。
这三种方式比较偏理论,下面介绍具体实现:
Object中equals方法要求如下:自反性,对称性,传递性,未修改则不变性,equals null为false性。
对于equals的对象,要求hashCode也要相等(算是一种规范吧,否则像HashSet这类使用起来会出现错误)。
对于可变对象来说,最规范的写法就是采用Object中的equals方法,举个例子来说:新建一个HashSet对象set,再新建一个ArrayList<String>对象list,list随便添加一个字符串,把list添加到set中,这时set.contains(list)返回true,list再添加一个字符串,set.contains(list)便返回false了,原因正是HashSet是依据hash table的原理,最开始根据其旧的hashCode存储,修改了list,其hashCode改变,依据新的hashCode再去set中找所以返回了一个错误的结果。
当然,最规范是这样的,但个人感觉这会抛弃很多实用的性质,所以个人见解是注意这个坑就好了,接下来给出两种等价性的思想:
1.观察等价。调用除了mutator(会改变对象状态的方法)之外没有差别。
2.行为等价。调用任何方法都没有差别。
实际上,在API的设计中这两种均有使用。但观察等价可能会导致一些微妙的错误,并且容易破坏集合类的表示不变性,即上面的例子,这应该也算集合设计的一个不足之处吧。
对于不可变类型,重写通常会带来好的性质,可以依据抽象值相等来重写,它也提供了行为等价的性质。
Object中equals方法要求如下:自反性,对称性,传递性,未修改则不变性,equals null为false性。
对于equals的对象,要求hashCode也要相等(算是一种规范吧,否则像HashSet这类使用起来会出现错误)。
对于可变对象来说,最规范的写法就是采用Object中的equals方法,举个例子来说:新建一个HashSet对象set,再新建一个ArrayList<String>对象list,list随便添加一个字符串,把list添加到set中,这时set.contains(list)返回true,list再添加一个字符串,set.contains(list)便返回false了,原因正是HashSet是依据hash table的原理,最开始根据其旧的hashCode存储,修改了list,其hashCode改变,依据新的hashCode再去set中找所以返回了一个错误的结果。
当然,最规范是这样的,但个人感觉这会抛弃很多实用的性质,所以个人见解是注意这个坑就好了,接下来给出两种等价性的思想:
1.观察等价。调用除了mutator(会改变对象状态的方法)之外没有差别。
2.行为等价。调用任何方法都没有差别。
实际上,在API的设计中这两种均有使用。但观察等价可能会导致一些微妙的错误,并且容易破坏集合类的表示不变性,即上面的例子,这应该也算集合设计的一个不足之处吧。
对于不可变类型,重写通常会带来好的性质,可以依据抽象值相等来重写,它也提供了行为等价的性质。