“比较方法违反了其总合同!” - 查找小样本数据集

时间:2012-11-04 12:44:45

标签: java sorting

  

可能重复:
  “Comparison method violates its general contract!”

我有一个更大的部分排序数据样本(> 700项)我想用Java 7排序并得到以下异常:

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)

现在我正在尝试降低数据集的大小,以便找到更简单的原因。我写了一个小应用程序,它从较大的集合中挑选一个随机子集来重现异常。

private static final int SUBSET_SIZE = 32;

public void testSorting() {
    ...
    final Random random = new Random();
    for (int i = 10000000; i-- > 0; ) {
        testFew(strings, random);
    }
}

private void testFew(List<String> strings, Random random) {
    final List<String> list = new ArrayList<String>();
    int index = 0;
    for (int i = 0; i < SUBSET_SIZE; i++) {
        final int rnd = random.nextInt(strings.size() / 100) + 1;
        index = (index + rnd) % strings.size();
        list.add(strings.get(index));
    }

    try {
        Collections.sort(list, MY_COMPARATOR);
    }
    catch (RuntimeException ex) {
        for (String s : list) {
            System.err.println(s);
        }
        throw ex;
    }
}

奇怪的是,如果子集包含至少32个项目,那么找到要重现的样本非常简单,但我没有成功找到一个较小的集合。恕我直言,这有点像排序算法中的错误而不是我们的比较器。

4 个答案:

答案 0 :(得分:7)

这意味着您的比较者有一个compareTo(a, b) != -compareTo(b, a)

的错误

答案 1 :(得分:5)

  恕我直言,这有点像排序算法中的错误而不是我们的比较器。

对我而言,根据输入集的大小,这会使用2种不同的排序算法。

虽然排序实现中存在错误并非不可能,但问题在于您的比较器更可能......就像异常消息所说的那样。建议您将精力集中在代码上,而不是在库代码中查找(可能不存在)错误。

答案 2 :(得分:2)

正如Stephen C已经猜到的那样,这是使用两种不同排序方法的结果。

查看java.util.TimSort的代码:

static <T> void sort(T[] a, Comparator<? super T> c) {
    sort(a, 0, a.length, c);
}

static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c) {

    // ...

    // If array is small, do a "mini-TimSort" with no merges
    if (nRemaining < MIN_MERGE) {
        int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
        binarySort(a, lo, hi, lo + initRunLen, c);
        return;
    }

    // ...

MIN_MERGE的值确实是32,抛出异常的方法只在另一种情况下被调用。

答案 3 :(得分:1)

我们的比较器中的错误 (它违反了A&lt; B&amp; B&lt; C&gt; A&lt; C),但我认为,TimSort将总是导致堆栈跟踪似乎是错误的。