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

imp*_*mps 6 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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

有任何想法吗?

小智 3

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

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

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

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

因此,该问题似乎与和TimSort中的compareTo 方法结合在一起。 java.util.Datejava.sql.Timestamp

然而,我没有必要去研究为什么会发生这种情况,因为它已经在 J​​ava 9 中修复了!

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

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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