[英]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)
I am sorting a collection based on the following comparator.我正在根据以下比较器对集合进行排序。
public static Comparator<MyClass> CMP_TIME_DESC = new Comparator<MyClass>() {
@Override
public int compare(MyClass o1, MyClass o2) {
return o2.getOrderSendTime().compareTo(o1.getOrderSendTime());
}
};
The values are always non-null.这些值总是非空的。 And the getOrderSendTime() object is of the java.util.Date class.
getOrderSendTime() 对象属于 java.util.Date 类。
I understand that this is a transitivity inconsistency, and I would assume a class like this would not have such issues.我知道这是传递性不一致,我认为这样的类不会有这样的问题。 I searched for open issues, but did not find any on the topic.
我搜索了未解决的问题,但没有找到有关该主题的任何问题。
Any ideas?有任何想法吗?
Your issue is related to this one: Sort algorithm changes in Java 7您的问题与此有关: Java 7 中的排序算法更改
It happens because the default sort algorithm has changed from MergeSort to TimSort .发生这种情况是因为默认排序算法已从 MergeSort 更改为 TimSort 。
One workaround is to add -Djava.util.Arrays.useLegacyMergeSort=true
to the JVM environment.一种解决方法是将
-Djava.util.Arrays.useLegacyMergeSort=true
添加到 JVM 环境。
The best option is to conform to the comparison general contract but I think you didn't provide enough information in your question for this.最好的选择是遵守比较一般合同,但我认为您在问题中没有为此提供足够的信息。
I had this same exception, and it happened when I had java.util.Date
and java.sql.Timestamp
objects in the same list/array when being sorted, running on Java8.我有同样的异常,当我在 Java8 上运行时在同一个列表/数组中有
java.util.Date
和java.sql.Timestamp
对象时发生了这种情况。 (This mix was due to some objects being loaded from database records with the Timestamp
data type, and others being created manually, and the objects having only a Date
object in them.) (这种混合是由于某些对象是从具有
Timestamp
数据类型的数据库记录中加载的,而其他对象是手动创建的,并且这些对象中只有一个Date
对象。)
The exception also doesn't happen every time you sort the same data set, and it seems that there also have to be at least 32 of these mixed objects in the array for it to occur.每次对同一数据集进行排序时也不会发生异常,而且似乎数组中至少必须有 32 个这些混合对象才能发生。
If I use the legacy sort algorithm, this also doesn't occur (see how in Ortomala Lokni's answer).如果我使用传统排序算法,这也不会发生(请参阅 Ortomala Lokni 的回答中的方法)。
This also doesn't happen if you use only java.util.Date
objects or only java.sql.Timestamp
objects in the array.如果在数组中仅使用
java.util.Date
对象或仅使用java.sql.Timestamp
对象,则也不会发生这种情况。
So, the issue seems to be TimSort
combined with the compareTo methods in java.util.Date
and java.sql.Timestamp
.因此,问题似乎是
TimSort
与java.util.Date
和java.sql.Timestamp
的 compareTo 方法相结合。
However, it didn't pay for me to research why this is happening since it is fixed in Java 9!但是,我没有为研究为什么会发生这种情况而付出代价,因为它已在 Java 9 中修复!
As a workaround until Java9 is released and we can get our systems updated, we have manually implemented a Comparator
that only uses getTime()
.作为 Java9 发布之前的一种解决方法,我们可以更新我们的系统,我们手动实现了一个仅使用
getTime()
的Comparator
。 This seems to work fine.这似乎工作正常。
Here is code that can be used to reproduce the issue:以下是可用于重现该问题的代码:
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);
}
}
}
Edit: I have removed the exception expectation so you can SEE it throwing when run.编辑:我已经删除了异常期望,所以你可以看到它在运行时抛出。
将“-Djava.util.Arrays.useLegacyMergeSort=true”添加到 VM 参数。
Faced this problem today, after spending a few hours, realized that I was comparing Longs.今天面对这个问题,花了几个小时后,意识到我在比较多头。 Instead, you need to compare Long.longValue().
相反,您需要比较 Long.longValue()。
MyData.utcTime() returns Long. MyData.utcTime() 返回 Long。 So instead of:
所以而不是:
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;
}
};
I used, the following to fix the problem.我使用了以下内容来解决问题。
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;
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.