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

java.lang.IllegalArgumentException: Comparison method violates its general contract! java.util.Date

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());
    }
};

值总是非空的。 而 getOrderSendTime() 对象属于 java.util.Date class.

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

有什么想法吗?

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

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

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

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

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

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

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

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

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

但是,我没有花钱研究为什么会发生这种情况,因为它已在 Java 9!

中修复

作为 Java9 发布之前的解决方法,我们可以更新系统,我们手动实施了仅使用 getTime()Comparator。这似乎工作正常。

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

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.junit.Test;

public class TimSortDateAndTimestampTest {

    // the same test data with all Dates, all Timestamps, all Strings or all Longs does NOT fail.
    // only fails with mixed Timestamp and Date objects
    @Test
    public void testSortWithTimestampsAndDatesFails() throws Exception {
        List<Date> dates = new ArrayList<>();
        dates.add(new Timestamp(1498621254602L));
        dates.add(new Timestamp(1498621254603L));
        dates.add(new Timestamp(1498621254603L));
        dates.add(new Timestamp(1498621254604L));
        dates.add(new Timestamp(1498621254604L));
        dates.add(new Timestamp(1498621254605L));
        dates.add(new Timestamp(1498621254605L));
        dates.add(new Timestamp(1498621254605L));
        dates.add(new Timestamp(1498621254605L));
        dates.add(new Timestamp(1498621254606L));
        dates.add(new Timestamp(1498621254607L));
        dates.add(new Date(1498621254605L));
        dates.add(new Timestamp(1498621254607L));
        dates.add(new Timestamp(1498621254609L));
        dates.add(new Date(1498621254603L));
        dates.add(new Date(1498621254604L));
        dates.add(new Date(1498621254605L));
        dates.add(new Date(1498621254605L));
        dates.add(new Date(1498621254607L));
        dates.add(new Timestamp(1498621254607L));
        dates.add(new Date(1498621254608L));
        dates.add(new Timestamp(1498621254608L));
        dates.add(new Date(1498621254611L));
        dates.add(new Timestamp(1498621254612L));
        dates.add(new Timestamp(1498621254613L));
        dates.add(new Date(1498621254607L));
        dates.add(new Timestamp(1498621254607L));
        dates.add(new Timestamp(1498621254608L));
        dates.add(new Timestamp(1498621254609L));
        dates.add(new Timestamp(1498621254611L));
        dates.add(new Date(1498621254603L));
        dates.add(new Date(1498621254606L));

        for (int i = 0; i < 200; i++) {
            Collections.shuffle(dates);
            Collections.sort(dates);
        }
    }
}

编辑: 我已经删除了异常预期,所以你可以看到它在 运行.

时抛出

将“-Djava.util.Arrays.useLegacyMergeSort=true”添加到 VM 参数。

今天遇到这个问题,花了几个小时后,意识到我在比较多头。相反,您需要比较 Long.longValue().

MyData.utcTime() returns 长。所以代替:

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;
    }
};