当前位置: 首页 > 知识库问答 >
问题:

java.lang.IllegalArgumentException:比较方法违反了其通用合同!java.util.Date

云何平
2023-03-14
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类。

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

有什么想法吗?

共有3个答案

巴照
2023-03-14

今天面对这个问题,在花了几个小时后,我意识到我在比较朗斯。相反,您需要比较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;
    }
};
洪开济
2023-03-14

您的问题与此有关:Java7中的排序算法更改

这是因为默认排序算法已从MergeSort更改为TimSort。

一种解决方法是将< code >-djava . util . arrays . uselegacymergesort = true 添加到JVM环境中。

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

程志新
2023-03-14

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

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

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

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

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

然而,我没钱研究为什么会发生这种情况,因为它是在Java9中修复的!

在Java9发布之前,作为一种变通方法,我们可以更新我们的系统,我们手动实现了一个只使用< code>getTime()的< code>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);
        }
    }
}

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

 类似资料:
  • 我知道很多答案已经回答了我的问题。在我的代码中,异常说“比较方法违反了它的一般合同”,但我不知道我的比较方法如何违反了它的一般合同。这是我的代码:

  • 我看到我的应用程序在一些中国 Android 手机上发生了很多崩溃,并出现错误:比较方法违反了其总合同! 我读过这与Collections.sort有关。 我不太确定的是,这是否是因为我的自定义比较器。 以下是错误发生的地方: 比较器是这样的: 所以我不太确定比较器是否搞砸了什么,或者我是否需要以不同的方式进行collections.sort调用 感谢任何帮助

  • 我已经在类上实现了Comaprable,它给了我比较方法违反了它的总合同!,由于有一些值返回为 null,代码如下 公共静态比较器名称比较器 = 新比较器() {

  • 我有一个类字段,和。我需要使用对它们进行排序,但我得到了一个异常: java.lang.IllegalArgumentException:比较方法违反了它的一般约定! 我的< code>compareTo方法: 请帮我找出compareTo方法中的错误。谢了。

  • 我收到以下错误:“比较方法违反了它的一般合同!”当使用下面的比较器时,我无法使用jUnit复制异常。我想知道是什么导致了这个问题,以及如何复制它。有其他人也有同样的问题,但不知道如何复制。 使用以下方法调用该代码: 感谢任何帮助。 额外信息:该错误似乎发生在Java utils中的TimSort类中,并来自一个名为mergeLo的方法。链接:http://grepcode.com/file/rep

  • 我目前正在Java中对集合进行排序。我收到了错误消息“比较方法违反了它的一般契约”。我也理解这个错误消息,但我(主要)使用Long类型的构建比较方法。所以我不知道,在这种情况下,排序方法仍然违反了契约。这是我的代码: 这里是错误: