2

考虑一个具有可比较(与 equals 一致)和不可比较字段(我不知道它是否覆盖的类Object#equals)的类。

应比较类的实例,其中结果顺序应与 equals 一致,即0如果两个字段相等(根据Object#equals)并且与可比较字段的顺序一致,则返回。我曾经System.identityHashCode涵盖了这些要求未涵盖的大多数情况(具有相同可比性但不同其他值的实例的顺序是任意的),但不确定这是否是最佳方法。

public class MyClass implements Comparable<MyClass> {
    private Integer intField;
    private Object nonCompField;

    public int compareTo(MyClass other) {
        int intFieldComp = this.intField.compareTo(other.intField);
        if (intFieldComp != 0)
            return intFieldComp;
        if (this.nonCompField.equals(other.nonCompField))
            return 0;
        // ...and now? My current approach:
        if (Systems.identityHashCode(this.nonCompField) < Systems.identityHashCode(other.nonCompField))
            return -1;
        else
            return 1;
     }
}

我在这里看到两个问题:

  • 如果Systems.identityHashCode两个对象相同,则每个对象都大于另一个对象。(这会发生吗?)
  • intField据我所知,具有相同值和不同值的实例的顺序nonCompField在程序运行之间不必保持一致Systems.identityHashCode

那是对的吗?还有更多问题吗?最重要的是,有没有办法解决这个问题?

4

3 回答 3

2

第一个问题虽然不太可能发生,但可能会发生(我认为您将需要大量内存,而且运气非常糟糕)。但它通过 Guava 的Ordering.arbitrary()解决了,它在幕后使用身份哈希码,但在两个不同对象具有相同身份哈希码的情况下维护比较结果的缓存。

关于您的第二个问题,不,运行之间不会保留身份哈希码。

于 2012-09-20T14:54:37.693 回答
1

Systems.identityHashCode[...] 两个对象相同 [...] (这会发生吗?)

是的,它可以。引用Java API 文档

在合理可行的情况下,hashCode类定义的方法Object确实为不同的对象返回不同的整数。
identityHashCode(Object x)为给定对象返回与默认方法返回的相同的哈希码hashCode(),无论给定对象的类是否覆盖hashCode()

因此,您可能会遇到哈希冲突,并且随着内存不断增长但哈希码保持固定在 32 位,它们将变得越来越有可能。

intField据我所知,具有相同值和不同值的实例的顺序nonCompField在程序运行之间不必保持一致Systems.identityHashCode

对。它甚至可能在同一个程序的单次调用中有所不同:(1,foo) < (1,bar) < (1,baz)即使foo.equals(baz).

最重要的是,有没有办法解决这个问题?

您可以维护一个映射,该映射将不可比较类型的每个不同值映射到一个序列号,您遇到的每个不同值都会增加该序列号。

但是,内存管理会很棘手:您不能使用 a WeakHashMap,因为代码可能会使您的关键对象无法访问,但仍然持有对另一个具有相同值的对象的引用。因此,要么你维护一个对给定值的所有对象的弱引用列表,要么你只是使用强引用并接受这样一个事实,即遇到的任何不可比较的值都不会被垃圾回收。

请注意,除非您以相同的顺序可重现地创建值,否则此方案仍不会产生可重现的序列号。

于 2012-09-20T14:54:48.337 回答
1

如果nonCompField 的类已经实现了一个相当好的 toString(),你也许可以使用

return String.valueOf(this.nonCompField).compareTo(String.valueOf(other.nonCompField));

不幸的是,默认的 Object.toString() 使用哈希码,这有其他人指出的潜在问题。

于 2012-09-20T15:25:27.883 回答