【问题标题】:Can/should one write a Comparator consistent with Object's equals method可以/应该写一个与 Object 的 equals 方法一致的 Comparator
【发布时间】:2015-06-09 16:24:47
【问题描述】:

我有一个对象Foo,它从Object 继承了默认的equals 方法,我不想重写它,因为引用相等是我想使用的身份关系。

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

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

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

【问题讨论】:

  • 如果两个 Foo 是同一个对象(例如,它们具有相同的引用),那么它们不能具有不同的字段值,对吧?因为它们是同一个对象?或者你说的是成员变量的引用?
  • 很难或不可能“比较”参考位置。
  • @DavidKoelle 你当然是正确的。如果foo1 == foo2,这意味着它们的字段值将相等,因此比任何基于字段值的等价关系更具区分性。我的问题是它更严格地歧视。因此,可能有 Foos 具有匹配值但不是同一个对象。
  • @LouisWasserman 嗯,这就是我害怕的。

标签: java equals comparator


【解决方案1】:

这在Comparator的文档中有描述:

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

在使用能够施加与等于不一致的排序的比较器来对已排序集(或已排序映射)进行排序时,应谨慎行事。假设带有显式比较器 c 的有序集合(或有序映射)与从集合 S 中提取的元素(或键)一起使用。如果 c 对 S 施加的排序与等于不一致,则有序集合(或有序映射)将表现得“奇怪”。特别是有序集合(或有序映射)将违反集合(或映射)的一般约定,该约定是用 equals 定义的。

简而言之,如果Comparator 的实现与equals 方法不一致,那么你应该知道你在做什么并且你要对这个设计的副作用负责,但这不是强加给使实现与Object#equals 一致。不过,请考虑到最好这样做,以免给将来维护系统的编码人员造成混乱。实现Comparable 时也适用类似的概念。

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


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

【讨论】:

  • 谢谢路易吉。我应该更清楚为什么我想让我的比较器与我的等号一致。 (我已按问题更新以添加此内容。)我了解使用与 equals 不一致的比较器的可能性,但理想情况下,我想避免将此类比较器与 SortedSet 一起使用所产生的影响。跨度>
  • SortedMap 的建议很好。尽管扩展以处理多个字段的多重比较时有些麻烦,但正如您所说,它确实提供了更多控制。本质上,它分离了对元素顺序的控制(根据键的顺序)和元素的包含(根据映射值中集合使用的 equals 方法)。我已将您的答案标记为已接受。感谢您的帮助。
  • @Dan 您还可以创建一个类KeyForYourClass implements Comparable<KeyForYourClass>,其中包含用于比较/排序YourClass 实例的相关字段,而不是使用单个字段。是的,它更麻烦,但遗憾的是 Java 中的选项。
【解决方案2】:

对于给定的类,您可能有多个比较器,即每个不同的字段。
在那种情况下equals不能被重用。因此答案是不一定。但是,如果您的集合存储在已排序(地图或树)中并且比较器用于确定该集合中的元素位置,则应该使它们保持一致。

详情请见documentation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多