ArrayList.sort()中的IllegalArgumentException - 方法

时间:2018-01-22 15:49:54

标签: java java-8 comparator

我得到了以下例外情况,但我并不清楚原因:

  

java.lang.IllegalArgumentException:比较方法违反了它   总合同!

     

在java.util.TimSort.mergeLo(TimSort.java:777)at at   java.util.TimSort.mergeAt(TimSort.java:514)at   java.util.TimSort.mergeCollapse(TimSort.java:441)at   java.util.TimSort.sort(TimSort.java:245)at   java.util.Arrays.sort(Arrays.java:1512)at   java.util.ArrayList.sort(ArrayList.java:1454)

我写了以下JUnit测试来验证行为:

@Test
public void testComparator() {
    List<Boolean> item = new ArrayList<>();

    item.add(true);
    for (int i = 0; i < 1000000; i++) {
        item.add(false);
    }

    while(true) {
        System.out.println("Sorting");
        Collections.shuffle(item);

        item.sort((lineItem1, lineItem2) -> {
            if (lineItem1 && lineItem2) {
                return 0;
            } else if (!lineItem1) {
                return 1; 
            } else if (!lineItem2 ) {
                return -1;
            } 

            return 0;
        });
    }
}

如果我交换返回1并返回-1,它突然无效。 但为什么?这应该只改变排序顺序而不是破坏整个比较器。

我错过了什么?

1 个答案:

答案 0 :(得分:7)

您的比较器违反了合同,因为当两个参数都是false时,由于语句1,它将返回if (!lineItem1) { return 1; }…

一般来说,没有保证TimSort会发现错误的比较器,它没有主动尝试查找合同违规,它只是将某些情况视为算法的副作用。

你真正想要的是

item.sort((lineItem1, lineItem2) -> {
    if (lineItem1.equals(lineItem2)) {
        return 0;
    } else if (!lineItem1) {
        return 1; 
    } else {
        return -1;
    }
});

虽然你可以通过

实现同样的目标
item.sort((lineItem1, lineItem2) -> lineItem1^lineItem2? lineItem1? -1: 1: 0);

甚至更简单

item.sort(Comparator.reverseOrder());