C#/ Java:在Equals测试引用标识时正确实现CompareTo

时间:2010-03-13 20:11:32

标签: c# java compare equals

我认为这个问题同样适用于C#和Java,因为两者都要求{c,C} ompare与{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是另一个想法,但我希望总是独特的东西,而不仅仅是大多数独特。

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

首先,如果您使用的是Java 5或更高版本,则应该实现Comparable<MyClass>而不是普通的Comparable,因此您的compareTo方法应采用MyClass类型的参数1}},而不是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;
        }
    }
}

关于你的问题,有效Java中的Josh Bloch(第3章,第12项)说:

  

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

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

这反过来意味着你可以拥有不相等的对象A和B,但是他们的比较会返回0.布洛赫先生对此有什么看法?

  

强烈建议(但并非严格要求)(x.compareTo(y)   == 0)==(x.equals(y))。一般来说,任何实现的类   应该明确指出Comparable接口和违反此条件的情况   这个事实。推荐的语言是“注意:这个课程很自然   与equals不一致的排序。“

  

compareTo方法强加订单的类   与equals不一致仍然可以工作,但排序的集合包含   该类别的元素可能不遵守相应集合的一般合同   接口(集合,集合或映射)。这是因为一般合同   这些接口是根据equals方法定义的,但是是已排序的集合   使用compareTo强加的相等测试代替equals。它不是   灾难,如果发生这种情况,但这是值得注意的事情。

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

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:1)

两个对象不需要引用等于在同一个等价类中。在我看来,对于两个不同的对象来说,进行比较是完全可以接受的,但是参考不一样。例如,对我来说,如果你从数据库中的同一行中加入两个不同的对象,那么它们似乎是完全自然的,为了进行比较,它们将是相同的,但不是引用相等。

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

答案 3 :(得分:0)

在我看来,通用等价物更易于处理,取决于您的外部要求,这是一个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值的情况下确定最终顺序。