【问题标题】:Unable to replicate : "Comparison method violates its general contract!"无法复制:“比较方法违反了它的总契约!”
【发布时间】:2015-04-25 12:55:28
【问题描述】:

我收到以下错误:“比较方法违反了它的一般合同!”使用以下比较器时,但是我无法使用 jUnit 复制异常。我想知道是什么导致了这个问题以及如何复制它。有其他人有同样问题但不知道如何复制它的例子。

public class DtoComparator implements Comparator<Dto> {

    @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

【问题讨论】:

  • 你们有非常大的订单(正的或负的)吗?
  • 嗨immibis,订单范围从1-20
  • 对于长度 >= 32 的列表为什么它失败的问题刚刚得到回答 here

标签: java sorting exception compare comparator


【解决方案1】:

来自compare的文档。

实施者必须确保所有xysgn(x.compareTo(y)) == -sgn(y.compareTo(x))

基于减法的比较器不满足此条件。这是因为减法会溢出。例如

Integer.MIN_VALUE - 0
0 - Integer.MIN_VALUE 

都是负数。

你处理Dates的方式也有问题。来自compare的文档:

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

您的 compare 方法打破了这一点。例如,如果 xnully 是 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);
} 

我设法重现了这个问题,但仅限于Lists 的长度至少为32。请参阅此链接以了解为什么需要大小至少为 32ListWhy does this program using Collections.sort only fail for lists of size 32 or more?

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<NumAndDate> {

        @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<NumAndDate> 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;
                }
            }
        }
    }
}

【讨论】:

  • 感谢您的建议,知道如何在测试中复制该问题吗?我创建了一个大列表,其中一些日期为空,但测试似乎总是工作正常。
  • @PhilHarper 我会考虑的。我可能会像在阅读您的问题之前一样,我不知道Collections.sort() 可能会抛出此异常。我一直认为如果比较器坏了,它会默默地产生一个毫无意义的结果。
  • @PhilHarper 我编写了一个程序来重现该问题。请参阅我的更新答案。
  • 非常感谢您的帮助,非常感谢!
猜你喜欢
  • 2018-03-05
  • 2022-01-04
  • 1970-01-01
  • 2012-10-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多