没有'正确'的compareTo方法在Java中对对象进行排序

时间:2016-03-17 11:42:40

标签: java

我们需要一个 compareTo 方法,它与不同类的对象一起工作,并保证如果对象不相同,它将永远不会返回0。

 Integer.compare(System.identityHashCode(o1), System.identityHashCode(o2))

不能在Java中使用 System.identityHashCode 并不能保证两个不同的对象会返回不同的identityHashCode(是的,是的,它会发生)。

一些想法?

1 个答案:

答案 0 :(得分:2)

由于不同的对象可以具有相同的System.identityHashCode()和hashCode(),所以无法确定该怎么做。你可以随意创建两个对象,对于这两个对象,使用Unsafe是相同的,如果你创建的则是随机的。

public class UnsafeIdentityDemo {

    static final Unsafe UNSAFE;

    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            UNSAFE = (Unsafe) theUnsafe.get(null);
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }

    public static void setIdentityHashCode(Object o, int code) {
        UNSAFE.putInt(o, 1l, code & 0x7FFF_FFF);
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Double d = 1.0;
        Double d2 = 1.0;
        setIdentityHashCode(d, 1);
        setIdentityHashCode(d2, 1);
        System.out.println("d: "+d+" System.identityHashCode(d): "+System.identityHashCode(d));
        System.out.println("d2: "+d2+" System.identityHashCode(d2): "+System.identityHashCode(d2));
        System.out.println("d == d2: " + (d == d2));
    }
}

打印

d: 1.0 System.identityHashCode(d): 1
d2: 1.0 System.identityHashCode(d2): 1
d == d2: false

您可以做的是根据发现的顺序分配对象(IntelliJ的调试器执行此操作)

public class UniversalComparator implements Comparator<Object> {
    @Override
    public int compare(Object o1, Object o2) {
        if (o1 == o2)
            return 0;
        int cmp = compare(o1.getClass(), o2.getClass());
        // if the classes are the same, and they are Comparable.
        if (cmp == 0 && o1 instanceof Comparable)
            cmp = ((Comparable) o1).compareTo(o2);
        // otherwise use the built in toString/hashCode/identityHashCode
        if (cmp == 0)
            cmp = Integer.compare(o1.toString(), o2.toString());
        if (cmp == 0)
            cmp = Integer.compare(o1.hashCode(), o2.hashCode());
        if (cmp == 0)
            cmp = Integer.compare(System.identityHashCode(o1), System.identityHashCode(o2));
        // otherwise generate a unique id for them
        if (cmp == 0)
            cmp = Integer.compare(uniqueId(o1), uniqueId(o2));
        return cmp;
    }

    final Map<Object, Integer>  uniqueId = new IdentityHashMap<>();
    private synchronized int uniqueId(Object o) {
        return uniqueId.computeIfAbsent(o, k -> uniqueId.size());
    }
}

这将确保所有不同类型的对象都是

  • 首先按类名排序。
  • 如果同一个班级和Comparable使用内置比较。
  • 否则,如果是同一个班级,则按toString()进行比较,然后hashCode(),然后System.identityHashCode
  • 否则会生成唯一ID。

这意味着对于大多数对象,您将获得可预测的排序顺序。

注意:这将慢慢构建所有冲突对象的地图。它会很小,但却是内存泄漏的潜在来源。 WeakIdentityHashMap会更好。