【问题标题】:How to reproduce a "Comparison method violates its general contract" IllegalArgumentException如何重现“比较方法违反其一般合同”IllegalArgumentException
【发布时间】:2013-06-07 14:52:09
【问题描述】:

我的一个应用程序曾经抛出一个 IllegalArgumentException,指出比较方法违反了它的一般合同。 我找到了一些详细说明问题的来源,例如 http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6804124http://www.oracle.com/technetwork/java/javase/compatibility-417013.html#source,并希望在我的应用程序中解决此问题。

但我无法重现该问题,因此无法知道我的修复是否正确。

在我努力复现的过程中,我尽量简化问题,并想出了一个看起来像这样的小类:

public class Sortee implements Comparable<Sortee>
{
  /** a value to sort by */
  public final int _x;

  public Sortee(int x)
  {
    _x = x;
  }

  public int compareTo(Sortee o)
  {
    return 1;
  }
}

我还创建了一个等价的比较器:

public class SorteeIncorrectComparator implements Comparator<Sortee>
{
  public int compare(Sortee a, Sortee b)
  {
    return 1;
  }
}

在另一个类中,我创建了一个 Sortee 对象列表并调用 Collections.sort() 变体来引发 IllegalStateException:

private static void sort()
{
   List<Sortee> sortees = createSortees();

   Collections.shuffle( sortees );
   Collections.sort( sortees, new SorteeIncorrectComparator() );

   Collections.shuffle( sortees );
   Collections.sort( sortees );
}

但永远不会引发 IllegalStateException。

我已经在 Linux 和 Windows 以及在 Windows 上使用 Java 1.7.0_21、23.21-b01 的 eclipse 中尝试过它 并检查属性 java.util.Arrays.useLegacyMergeSort 没有设置。

我认为在 compare 方法中总是返回 1 应该会破坏契约,因为它既不交换也不传递。

为什么我从来没有收到 IllegalStateException?

【问题讨论】:

  • 我不确定我是否看到编写全新代码以重现其他代码中出现的问题的好处。为什么不发布引发异常的代码,让我们发现错误?

标签: java comparator comparable illegalstateexception


【解决方案1】:
public static void main(String[] args) {
    Object[] array = new Object[37];
    for (int i = 0; i < array.length; i++) {
        array[i] = new Object();
    }
    Arrays.sort(array, new Comparator<Object>() {
        private int result[] = {1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, 0, -1, -1, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
        private int index;
        @Override
        public int compare(Object o1, Object o2) {
            return result[index++];
        }
    });
}

【讨论】:

  • 艾哈迈德,你能解释一下你的认识吗? result[] - 这里是随机值还是一些逻辑?谢谢。
【解决方案2】:

当比较器违反 a=b 和 b=c -> a=c 时,我能够重现这一点,然后 timsort 似乎在抱怨。我的测试是:

List<Integer> timSortTestList = new ArrayList<Integer>();
{
    for(int i=0; i<100; ++i) {
        timSortTestList.add(i);
        timSortTestList.add(i);
        timSortTestList.add(i);
    }
    Collections.shuffle(timSortTestList, new Random(42));
}
Comparator<Integer> broken = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        if (Math.abs(o1-o2) < 10) {
            return Compare.EQUAL; // WRONG
        }
        return Ordering.natural().compare(o1, o2);
    }
};
Collections.sort(timSortTestList, broken); // throws up

比较 this question - 发生这种情况时可能有一个通用规则。

【讨论】:

  • 对该问题的答案的分析表明(如果我解释正确的话)排序方法在对短序列进行排序时永远不会抛出异常。所以你的答案的关键部分是你的列表有 300 个元素长。
  • 如果数组少于MIN_MERGE=32 条目,优化的合并排序(Comparable)TimSort 将使用最小化版本(使用binarySort)。如果您指定更多元素,则取决于排序候选者的分布是否符合条件(i
【解决方案3】:

我的实现实际上是可传递的。

我已将比较方法更改为返回随机值并引发异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多