【问题标题】:C#/Java: Proper Implementation of CompareTo when Equals tests reference identityC#/Java:当 Equals 测试引用标识时正确实现 CompareTo
【发布时间】:2010-03-13 20:11:32
【问题描述】:

我相信这个问题同样适用于 C# 和 Java,因为两者都要求 {c,C}ompareTo 与 {e,E}quals:

假设我希望我的 equals() 方法与参考检查相同,即:

public bool equals(Object o) {
    return this == o;
}

在这种情况下,我如何实现 compareTo(Object o)(或其通用等效项)?其中一部分很容易,但我不确定另一部分:

public int compareTo(Object o) {
    MyClass other = (MyClass)o;
    if (this == other) {
        return 0;
    } else {
        int c = foo.CompareTo(other.foo)
        if (c == 0) {
            // what here?
        } else {
            return c;
        }
    }
}

我不能只是盲目地返回 1 或 -1,因为解决方案应该遵守 compareTo 的正常要求。我可以检查所有实例字段,但如果它们都相等,我仍然希望 compareTo 返回一个非 0 的值。 a.compareTo(b) == -(b.compareTo(a) 应该是真的),并且只要对象的状态没有改变,顺序就应该保持一致。

不过,我不关心虚拟机调用的顺序。这让我觉得我可以使用像内存地址这样的东西,如果我能做到的话。再说一次,也许这行不通,因为垃圾收集器可以决定移动我的对象。

hashCode 是另一个想法,但我想要一些始终独特的东西,而不仅仅是大部分独特的东西。

有什么想法吗?

【问题讨论】:

  • 我不明白。如果对象没有合理的逻辑排序,那你为什么还需要排序呢?如果您的对象在逻辑上不能相互比较,请不要费心实现 Comparable。你想解决什么问题?
  • 您可以使用 Object.ReferenceEquals(a,b) 来检查它是否是完全相同的对象。
  • @Joren:我的对象确实有逻辑顺序,但是一旦我完成了它们的所有状态,当 equals 检查引用相等性时,我仍然需要 compareTo 与 equals 保持一致。
  • 您不需要检查对象的实例,因为 compareTo 允许抛出 CastClassException(与 equals() 不同)。此外,return false 在这里是不合法的 - 这可能是从您的 equals 方法之一复制而来的?
  • 保罗,如果他们有一个逻辑顺序,那么把它放在你的 equals 方法中。在 C# 中,您始终可以使用 Java == 运算符和 Object.ReferenceEquals() 获得引用等于。任何一种语言的引用都不能使用 > (+1) 或

标签: c# java compare equals


【解决方案1】:

首先,如果您使用 Java 5 或更高版本,您应该实现 Comparable<MyClass> 而不是普通的旧 Comparable,因此您的 compareTo 方法应该采用 MyClass 类型的参数,而不是Object

public int compareTo(MyClass other) {
    if (this == other) {
        return 0;
    } else {
        int c = foo.CompareTo(other.foo)
        if (c == 0) {
            // what here?
        } else {
            return c;
        }
    }
}

关于您的问题,Effective Java (Chapter 3, Item 12) 中的 Josh Bloch 说:

实施者必须确保 sgn(x.compareTo(y)) == -sgn(y.compare- 所有 x 和 y 的 To(x))。 (这意味着 x.compareTo(y) 必须抛出异常 当且仅当 y.compareTo(x) 抛出异常。)

这意味着如果上面代码中c == 0,则必须返回0。

这反过来意味着你可以有对象 A 和 B,它们不相等,但它们的比较返回 0。Bloch 先生对此有什么要说的?

强烈建议但不严格要求 (x.compareTo(y) == 0) == (x.equals(y))。一般来说,任何实现的类 Comparable 接口并违反此条件应清楚地表明 这个事实。推荐的语言是“注意:这个类有一个自然的 与equals不一致的顺序。”

一个类,其 compareTo 方法强加一个顺序 与 equals 不一致的仍然可以工作,但包含的已排序集合 类的元素可能不遵守适当集合的一般约定 接口(集合、集合或映射)。这是因为一般合同 因为这些接口是根据 equals 方法定义的,但已排序的集合 使用 compareTo 强加的相等性测试代替 equals。它不是一个 如果发生这种情况会造成灾难,但需要注意。

更新:所以恕我直言,对于您当前的课程,您不能使 compareToequals 一致。如果您真的需要这个,我看到的唯一方法是引入一个新成员,这将为您的班级提供严格的自然排序。那么如果两个对象的所有有意义的字段都比较为 0,您仍然可以根据它们的特殊顺序值来决定两者的顺序。

这个额外的成员可能是实例计数器或创建时间戳。或者,您可以尝试使用UUID

【讨论】:

  • 我的问题恰恰是当 equals 只是测试引用相等性时,如何使 compareTo 与 equals 保持一致?
  • 嗯,计数器和唯一 ID 都是可能的想法。你知道 C# 是否有类似 Java 的唯一 ID 的东西吗?
  • @Paul A Jungwirth 不幸的是我对 C# 不太熟悉 :-(
  • 经过一番谷歌搜索后,我实际上看不到任何在 Java 中获取唯一对象 ID 的方法。有些人错误地建议使用identityHashCode,但不能保证是唯一的。这意味着我必须从静态计数器中设置一个字段,在多线程环境中必须同步该字段。 . . .看起来鲁本提出了所有这些观点!
  • @Paul A Jungwirth 是的,他的帖子很棒,值得 +1 :-) 我确实对 toString 的事情有误,我修正了我的答案。我仍然有这种模糊的记忆,虽然关于几周前我一定读过的东西,我就是找不到。
【解决方案2】:

在 Java 或 C# 中,一般来说,对象没有固定的顺序。垃圾收集器在执行您的 compareTo 或使用您的 compareTo 的排序操作时可以移动实例。

正如您所说,哈希码通常不是唯一的,因此它们不可用(具有相同哈希码的两个不同实例让您回到最初的问题)。许多人认为会显示对象 id (MyObject@33c0d9d) 的 Java Object.toString 实现只不过是对象的类名后跟散列码。据我所知,JVM 和 CLR 都没有实例 ID 的概念。

如果您真的希望类的顺序一致,您可以尝试为您创建的每个新实例使用递增的数字。请注意,递增这个计数器必须是线程安全的,所以它会相对昂贵(在 C# 中你可以使用 Interlocked.Increment)。

【讨论】:

    【解决方案3】:

    两个对象不需要被引用等于在同一个等价类中。在我看来,两个不同的对象在比较中相同但引用不相等应该是完全可以接受的。例如,在我看来,如果您对数据库中同一行中的两个不同对象进行水合,那么它们将是相同的,用于比较目的,但引用不相等。

    我实际上更倾向于修改 equals 的行为以反映它们的比较方式,而不是相反。对于我能想到的大多数目的,这会更自然。

    【讨论】:

      【解决方案4】:

      在我看来,通用等价物更容易处理,这取决于您的外部要求,这是一个 IComparable<MyClass> 示例:

      public int CompareTo(MyClass other) {
          if (other == null) return 1;
          if (this == other) {
              return 0;
          } else {
              return foo.CompareTo(other.foo);
          }
      }
      

      如果类相等或foo 相等,则比较结束,除非有次要的排序,在这种情况下,如果foo.CompareTo(other.foo) == 0 将其添加为返回

      如果您的课程有 ID 或其他内容,则将其作为次要进行比较,否则不要担心...他们存储它的集合以及到达这些课程的顺序以进行比较是什么确定对象相等或object.foo值相等的情况下的最终顺序。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-09
        • 1970-01-01
        • 1970-01-01
        • 2011-10-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多