可以/应该编写一个与Object的equals方法一致的Comparator

时间:2015-06-09 16:24:48

标签: java equals comparator

我有一个对象Fooequals继承了默认的Object方法,我不想覆盖它,因为引用相等是我想要的身份关系使用。

我现在有一个特定的情况,我现在想根据特定字段比较这些对象。我想编写一个比较器FooValueComparator来执行此比较。但是,如果我的FooValueComparator返回0,只要两个对象具有相同的特定字段的值,那么它与从Object继承的equals方法不兼容,以及所有需要的问题。

我想要做的是让FooValueComparator首先比较两个对象的字段值,然后再比较它们的引用。这可能吗?可能存在哪些陷阱(例如,内存位置发生变化导致Foo个对象的相对顺序发生变化)?

我希望我的比较器与equals兼容的原因是因为我希望可以选择将它应用于SortedSetFoo个对象的集合。我不希望SortedSet拒绝我尝试添加的Foo因为它已经包含具有相同值的其他对象。

2 个答案:

答案 0 :(得分:3)

Comparator的文档中描述了这一点:

  

当且仅当S具有与{相同的布尔值}时,比较器c对一组元素c.compare(e1, e2)==0施加的排序被称为与等于一致对于e1.equals(e2)中的每个e1e2,{1}}。

     

当使用能够强加与equals不一致的排序的比较器来排序有序集(或有序映射)时,应该小心。假设带有显式比较器c的有序集(或有序映射)与从集合S中绘制的元素(或键)一起使用。如果由S对S施加的排序与equals不一致,则排序集(或有序映射)将表现得很奇怪。"特别是有序集(或有序映射)将违反集合(或映射)的一般契约,它以等于的方式定义。

简而言之,如果S的实施方式与Comparator方法不一致,那么您应该知道自己在做什么,并且您要对此产生的副作用负责设计,但并不是要使实施与equals保持一致。尽管如此,考虑到最好这样做是为了不会对维护系统的未来编码人员造成混淆。实施Object#equals时也适用类似的概念。

JDK中的一个示例可以在BigDecimal#compareTo中找到,它在javadoc中明确指出此方法与Comparable不一致。

如果您打算使用BigDecimal#equals,那么您可能会使用错误的方法。我建议使用SortedSet<YourClass>(或SortedMap<TypeOfYourField, Collection<YourClass>>,以防相同的密钥没有等于元素)。这可能需要做更多的工作,但它可以让您更好地控制在结构中存储/检索的数据。

答案 1 :(得分:1)

对于给定的类,您可能有几个比较器,即每个不同的字段。
在这种情况下,等于不能重复使用。因此答案是不一定。如果你的集合存储在一个有序的(map或tree)中,并且comperator用于确定该集合中的元素位置,你应该使它们一致。

有关详细信息,请参阅documentation