无法复制:“比较方法违反了其总合同!”

我收到以下错误:“比较方法违反了其总合同!” 使用以下比较器时,我无法使用jUnit复制exception。 我想知道导致这个问题的原因以及如何复制它。 其他一些例子有相同的问题而不是如何复制它。

public class DtoComparator implements Comparator { @Override public int compare(Dto r1, Dto r2) { int value = 0; value = r1.getOrder() - r2.getOrder(); if (value == 0 && !isValueNull(r1.getDate(), r2.getDate())) value = r1.getDate().compareTo(r2.getDate()); return value; } private boolean isValueNull(Date date, Date date2) { return date == null || date2 == null; } } 

使用以下代码调用代码:

 Collections.sort(dtos, new DtoComparator()); 

谢谢你的帮助。

额外信息:错误似乎发生在Java utils内部的TimSort类中以及名为mergeLo的方法中。 链接: http : //grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/TimSort.java#TimSort.mergeLo%28int%2Cint%2Cint%2Cint% 29

compare文档。

对于所有xy ,实现者必须确保sgn(x.compareTo(y)) == -sgn(y.compareTo(x))

基于减法的比较器不符合这种条件。 这是因为减法可能会溢出。 例如

 Integer.MIN_VALUE - 0 0 - Integer.MIN_VALUE 

都是消极的。

您处理Date的方式也存在问题。 从compare文档:

最后,对于所有z ,实现者必须确保x.compareTo(y)==0意味着sgn(x.compareTo(z)) == sgn(y.compareTo(z))

你的compare方法打破了这个。 例如,如果xnull ,则y为1970年1月1日, z为1970年1月2日

 compare(x, y) == 0 // x == null compare(x, z) == 0 // x == null compare(y, z) == -1 // January 1st is before January 2nd. 

我会写如下方法:

 @Override public int compare(Dto r1, Dto r2) { int value = Integer.compare(r1.getOrder(), r2.getOrder()); if (value != 0) return value; Date date1 = r1.getDate(); Date date2 = r2.getDate(); if (date1 == null && date2 == null) return 0; if (date1 == null) return -1; if (date2 == null) return 1; return date1.compareTo(date2); } 

我设法重现了这个问题,但仅限于长度至少为32 List 。 请参阅此链接以获取有关为何需要至少32大小的List的说明。 为什么使用Collections.sort的程序仅对32或更大的列表失败?

 public class Main { private static final class NumAndDate { private final int num; private final Date date; NumAndDate(int num, Date date) { this.num = num; this.date = date; } } public static final class NumAndDateComparator implements Comparator { @Override public int compare(NumAndDate r1, NumAndDate r2) { int value = 0; value = r1.num - r2.num; if (value == 0 && !isValueNull(r1.date, r2.date)) value = r1.date.compareTo(r2.date); return value; } private boolean isValueNull(Date date, Date date2) { return date == null || date2 == null; } } public static void main(String[] args) { NumAndDate[] array = { new NumAndDate(0, new Date(0)), new NumAndDate(0, new Date(1)), new NumAndDate(0, null) }; Random random = new Random(); for (int i = 0; i < 100; i++) { for (int j = 0; j < 10000; j++) { List list = new ArrayList<>(); int[] arr = new int[i]; for (int k = 0; k < i; k++) { int rand = random.nextInt(3); arr[k] = rand; list.add(array[rand]); } try { Collections.sort(list, new NumAndDateComparator()); } catch (Exception e) { System.out.println(arr.length + " " + Arrays.toString(arr)); return; } } } } }