C#/Java:当 Equals 测试引用标识时正确实现 CompareTo
我相信这个问题同样适用于 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 是另一个想法,但我想要一些始终唯一的东西,而不仅仅是大部分唯一。
有什么想法吗?
I believe this question applies equally well to C# as to Java, because both require that {c,C}ompareTo be consistent with {e,E}quals:
Suppose I want my equals() method to be the same as a reference check, i.e.:
public bool equals(Object o) {
return this == o;
}
In that case, how do I implement compareTo(Object o) (or its generic equivalent)? Part of it is easy, but I'm not sure about the other part:
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;
}
}
}
I can't just blindly return 1 or -1, because the solution should adhere to the normal requirements of compareTo. I can check all the instance fields, but if they are all equal, I'd still like compareTo to return a value other than 0. It should be true that a.compareTo(b) == -(b.compareTo(a)), and the ordering should stay consistent as long as the objects' state doesn't change.
I don't care about ordering across invocations of the virtual machine, however. This makes me think that I could use something like memory address, if I could get at it. Then again, maybe that won't work, because the Garbage Collector could decide to move my objects around.
hashCode is another idea, but I'd like something that will be always unique, not just mostly unique.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
首先,如果您使用的是 Java 5 或更高版本,您应该实现
Comparable
而不是普通的旧Comparable
,因此您的compareTo
方法应该采用MyClass
类型的参数,而不是Object
:就你的问题而言,Josh Bloch 在《Effective Java》(第 3 章,第 12 项)中说:
这意味着如果上面代码中的 c == 0,则必须返回 0。
这又意味着您可以拥有对象 A 和 B,但它们不相等,但它们的比较返回 0。布洛赫先生对此有何评论?
和
更新:因此恕我直言,对于您当前的类,您无法使
compareTo
与equals
一致。如果您确实需要这个,我认为唯一的方法是引入一个新成员,这将为您的班级提供严格的自然顺序。然后,如果两个对象的所有有意义字段都比较为 0,您仍然可以根据它们的特殊顺序值来决定两个对象的顺序。这个额外的成员可以是实例计数器或创建时间戳。或者,您可以尝试使用 UUID。
First of all, if you are using Java 5 or above, you should implement
Comparable<MyClass>
rather than the plain oldComparable
, therefore yourcompareTo
method should take parameters of typeMyClass
, notObject
:As of your question, Josh Bloch in Effective Java (Chapter 3, Item 12) says:
This means that if c == 0 in the above code, you must return 0.
That in turn means that you can have objects A and B, which are not equal, but their comparison returns 0. What does Mr. Bloch have to say about this?
And
Update: So IMHO with your current class, you can not make
compareTo
consistent withequals
. If you really need to have this, the only way I see would be to introduce a new member, which would give a strict natural ordering to your class. Then in case all the meaningful fields of the two objects compare to 0, you could still decide the order of the two based on their special order values.This extra member may be an instance counter, or a creation timestamp. Or, you could try using a UUID.
一般来说,在 Java 或 C# 中,对象没有固定的顺序。在执行compareTo 或使用compareTo 的排序操作时,垃圾收集器可以移动实例。
正如您所说,哈希码通常不是唯一的,因此它们不可用(具有相同哈希码的两个不同实例使您回到原来的问题)。许多人认为 Java Object.toString 实现可以显示对象 id (MyObject@33c0d9d),它只不过是对象的类名后跟哈希码。据我所知,JVM 和 CLR 都没有实例 id 的概念。
如果您确实希望类的顺序保持一致,您可以尝试为您创建的每个新实例使用递增的数字。请注意,递增此计数器必须是线程安全的,因此它的成本相对较高(在 C# 中,您可以使用 Interlocked.Increment)。
In Java or C#, generally speaking, there is no fixed ordering of objects. Instances can be moved around by the garbage collector while executing your compareTo, or the sort operation that's using your compareTo.
As you stated, hash codes are generally not unique, so they're not usable (two different instances with the same hash code bring you back to the original question). And the Java Object.toString implementation which many people believe to surface an object id (MyObject@33c0d9d), is nothing more than the object's class name followed by the hash code. As far as I know, neither the JVM nor the CLR have a notion of an instance id.
If you really want a consistent ordering of your classes, you could try using an incrementing number for each new instance you create. Mind you, incrementing this counter must be thread safe, so it's going to be relatively expensive (in C# you could use Interlocked.Increment).
两个对象不需要引用相等才能位于同一等价类中。在我看来,两个不同的对象相同进行比较应该是完全可以接受的,但引用不相等。例如,对我来说,如果您对数据库中同一行的两个不同对象进行水合,那么出于比较目的,它们将是相同的,但引用不相等。
实际上,我更倾向于修改 equals 的行为以反映它们的比较方式,而不是相反。对于我能想到的大多数目的来说,这会更自然。
Two objects don't need to be reference equal to be in the same equivalence class. In my opinion, it should be perfectly acceptable for two different objects to be the same for a comparison, but not reference equal. It seems perfectly natural to me, for example, that if you hydrated two different objects from the same row in the database, that they would be the same for comparison purposes, but not reference equal.
I'd actually be more inclined to modify the behavior of equals to reflect how they are compared rather than the other way around. For most purposes that I can think of this would be more natural.
在我看来,通用等效项更容易处理,具体取决于您的外部要求是什么,这是一个
IComparable
示例:如果类相等或 if
foo
相等,这就是比较的结束,除非有次要的东西要排序,在这种情况下,将其添加为返回 iffoo.CompareTo(other.foo) == 0
如果您的类有ID之类的东西,然后将其作为辅助进行比较,否则不用担心......他们存储的集合以及到达这些类进行比较的顺序将决定在以下情况下的最终顺序相等的对象或相等的
object.foo
值。The generic equivalent is easier to deal with in my opinion, depends what your external requirements are, this is a
IComparable<MyClass>
example:If the classes are equal or if
foo
is equal, that's the end of the comparison, unless there's something secondary to sort on, in that case add it as the return iffoo.CompareTo(other.foo) == 0
If your classes have an ID or something, then compare on that as secondary, otherwise don't worry about it...the collection they're stored it and it's order in arriving at these classes to compare is what's going to determine the final order in the case of equal objects or equal
object.foo
values.