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)不会有这样的问题。我搜索了 Unresolved 问题,但未找到关于该主题的任何内容。

有任何想法吗?

最佳答案

我有同样的异常(exception),当在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之前的解决方法,我们可以更新系统,我们已经手动实现了仅使用ComparatorgetTime()。这似乎工作正常。

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

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

编辑:我已经删除了异常期望,因此您可以在运行时看到它抛出的异常。

09-26 22:52
查看更多