java.lang.IllegalArgumentException:比较方法违反了它的一般约定!日期类型
我正在根据以下比较器对集合进行排序java.lang.IllegalArgumentException:比较方法违反了它的一般约定!日期类型,java,sorting,date,timsort,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(TimSor
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)
公共静态比较器CMP\u TIME\u DESC=新比较器(){
@凌驾
公共整数比较(MyClass o1,MyClass o2){
返回o2.getOrderSendTime().compareTo(o1.getOrderSendTime());
}
};
这些值总是非空的。
getOrderSendTime()对象属于java.util.Date类
我知道这是一个传递性不一致的问题,我假设这样的类不会有这样的问题。我搜索了公开的问题,但没有找到任何关于这个主题的
有什么想法吗?您的问题与此相关: 发生这种情况是因为默认的排序算法已被修改 一种解决方法是向JVM环境添加
-Djava.util.Arrays.useLegacyMergeSort=true
最好的选择是遵守比较总合同,但我认为您在问题中没有为此提供足够的信息 我也有同样的异常,当我在Java8上运行排序时,在同一个列表/数组中有
java.util.Date
和java.sql.Timestamp
对象时,就会发生这种异常。(这种混合是由于一些对象是从带有时间戳
数据类型的数据库记录加载的,而其他对象是手动创建的,并且这些对象中只有日期
对象。)
也不是每次对同一数据集进行排序时都会发生异常,而且似乎数组中还必须至少有32个这样的混合对象才能发生异常
如果我使用传统的排序算法,这也不会发生(请参见Ortomala Lokni的答案中的说明)
如果在数组中仅使用java.util.Date
对象或仅使用java.sql.Timestamp
对象,也不会发生这种情况
因此,问题似乎是TimSort
与java.util.Date
和java.sql.Timestamp
中的compareTo方法相结合
但是,我没有研究为什么会发生这种情况,因为它在Java 9中是固定的强>
在Java9发布之前,作为一种变通方法,我们可以更新我们的系统,我们已经手动实现了一个只使用getTime()
的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());
}
};
导入java.sql.Timestamp;
导入java.util.ArrayList;
导入java.util.Collections;
导入java.util.Date;
导入java.util.List;
导入org.junit.Test;
公共类TIMSORTDATE和TIMESTATEST测试{
//具有所有日期、所有时间戳、所有字符串或所有长度的相同测试数据不会失败。
//仅在时间戳和日期对象混合时失败
@试验
public void testSortWithTimestampsAndDatesFails()引发异常{
列表日期=新建ArrayList();
添加(新时间戳(1498621254602L));
添加(新时间戳(1498621254603L));
添加(新时间戳(1498621254603L));
添加(新时间戳(1498621254604L));
添加(新时间戳(1498621254604L));
添加(新时间戳(1498621254605L));
添加(新时间戳(1498621254605L));
添加(新时间戳(1498621254605L));
添加(新时间戳(1498621254605L));
添加(新时间戳(1498621254606L));
添加(新时间戳(1498621254607L));
添加日期(新日期(1498621254605L));
添加(新时间戳(1498621254607L));
添加(新时间戳(1498621254609L));
添加日期(新日期(1498621254603L));
添加日期(新日期(1498621254604L));
添加日期(新日期(1498621254605L));
添加日期(新日期(1498621254605L));
添加日期(新日期(1498621254607L));
添加(新时间戳(1498621254607L));
添加日期(新日期(1498621254608L));
添加(新时间戳(1498621254608L));
添加日期(新日期(1498621254611L));
添加(新时间戳(1498621254612L));
添加(新时间戳(1498621254613L));
添加日期(新日期(1498621254607L));
添加(新时间戳(1498621254607L));
添加(新时间戳(1498621254608L));
添加(新时间戳(1498621254609L));
添加(新时间戳(1498621254611L));
添加日期(新日期(1498621254603L));
添加日期(新日期(1498621254606L));
对于(int i=0;i<200;i++){
收藏。洗牌(日期);
集合。排序(日期);
}
}
}
编辑:我已经删除了异常预期,因此您可以看到它在运行时抛出。将“-Djava.util.Arrays.useLegacyMergeSort=true”添加到VM参数。今天遇到这个问题,花了几个小时后,意识到我在比较长。相反,您需要比较Long.longValue() MyData.utcTime()返回Long。因此,不是:
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);
}
}
}
public static Comparator sortByUtcPublishedDesc=new Comparator(){
@凌驾
公共整数比较(MyData n1,MyData n2){
如果(n2.utcTime()n1.utcTime())
返回1;
}
};
我使用以下方法来解决这个问题
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 sortByUtcPublishedDesc=new Comparator(){
@凌驾
公共整数比较(MyData n1,MyData n2){
如果(n2.utcTime().longValue()n1.utcTime())
返回1;
}
};
您是否绝对确定在排序过程中没有其他线程(或者只是getOrderSendTime()
中的一些计算会产生副作用)修改日期?是的,对象是ob
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;
}
};