java.lang.IllegalArgumentException:比较方法违反了它的一般合同! java.util.Date

时间:2015-07-07 16:41:22

标签: java sorting date timsort

java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.TimSort.mergeLo(TimSort.java:747)
    at java.util.TimSort.mergeAt(TimSort.java:483)
    at java.util.TimSort.mergeCollapse(TimSort.java:410)
    at java.util.TimSort.sort(TimSort.java:214)
    at java.util.TimSort.sort(TimSort.java:173)
    at java.util.Arrays.sort(Arrays.java:659)
    at java.util.Collections.sort(Collections.java:217)

我正在根据以下比较器对集合进行排序。

public static Comparator<MyClass> CMP_TIME_DESC = new Comparator<MyClass>() {
    @Override
    public int compare(MyClass o1, MyClass o2) {
        return o2.getOrderSendTime().compareTo(o1.getOrderSendTime());
    }
};

值始终为非null。 getOrderSendTime()对象属于java.util.Date类。

我知道这是一个传递性不一致的地方,我认为像这样的课程不会有这样的问题。我搜索了未解决的问题,但没有找到关于该主题的任何内容。

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

您的问题与此问题有关:Sort algorithm changes in Java 7

这是因为默认排序算法有changed from MergeSort to TimSort

一种解决方法是将-Djava.util.Arrays.useLegacyMergeSort=true添加到JVM环境中。

最好的选择是遵守比较一般合同,但我认为您没有在此问题中提供足够的信息。

答案 1 :(得分:3)

我有同样的异常,当我在Java8上运行排序时,我在同一列表/数组中有java.util.Datejava.sql.Timestamp个对象时发生了这种情况。 (这种混合是由于某些对象是从具有Timestamp数据类型的数据库记录加载的,而其他对象是手动创建的,而对象中只有Date个对象。)

每次对同一数据集进行排序时也不会发生异常,并且似乎阵列中至少还有32个这样的混合对象才会发生。

如果我使用传统排序算法,这也不会发生(请参阅Ortomala Lokni的回答)。

如果您只使用数组中的java.util.Date个对象或java.sql.Timestamp个对象,也不会发生这种情况。

因此,问题似乎是TimSortjava.util.Datejava.sql.Timestamp中的compareTo方法相结合。

然而,由于它已在Java 9中得到修复,因此我无法研究为什么会发生这种情况!

作为一种解决方法,直到Java9发布并且我们可以更新系统,我们已手动实施仅使用Comparator的{​​{1}}。这似乎工作正常。

以下是可用于重现问题的代码:

getTime()

修改:我已删除异常预期,因此您可以在运行时看到它抛出。

答案 2 :(得分:0)

添加&#34; -Djava.util.Arrays.useLegacyMergeSort = true&#34;到VM参数。

答案 3 :(得分:0)

在花了几个小时之后,今天面对了这个问题,意识到我正在比较Longs。相反,您需要比较Long.longValue()。

MyData.utcTime()返回Long。所以代替:

public static Comparator<MyData> sortByUtcPublishedDesc = new Comparator<MyData>() {
    @Override
    public int compare(MyData n1, MyData n2) {
        if ( n2.utcTime() < n1.utcTime() )
            return -1;
        if ( n2.utcTime() == n1.utcTime() )
            return 0;
        // if (n2.utcTime() > n1.utcTime())
        return 1;
    }
};

我用以下方法解决问题。

public static Comparator<MyData> sortByUtcPublishedDesc = new Comparator<MyData>() {
    @Override
    public int compare(MyData n1, MyData n2) {
        if ( n2.utcTime().longValue() < n1.utcTime().longValue() )
            return -1;
        if ( n2.utcTime().longValue() == n1.utcTime().longValue() )
            return 0;
        // if (n2.utcTime() > n1.utcTime())
        return 1;
    }
};