当前位置: 首页 > 面试题库 >

为什么使用Collections.sort的该程序仅对大小为32或更大的列表失败?

唐宇定
2023-03-14
问题内容

以下程序引发以下异常:

java.lang.IllegalArgumentException: Comparison method violates its general contract!

我了解的问题Comparator。请参阅无法复制:“比较方法违反了它的一般约定!”

我不明白为什么它只会List在大小为32或更大的情况下失败。谁能解释?

class Experiment {

    private static final class MyInteger {
        private final Integer num;

        MyInteger(Integer num) {
            this.num = num;
        }
    }

    private static final Comparator<MyInteger> COMPARATOR = (r1, r2) -> {
        if (r1.num == null || r2.num == null)
            return 0;
        return Integer.compare(r1.num, r2.num);
    };

    public static void main(String[] args) {
        MyInteger[] array = {new MyInteger(0), new MyInteger(1), new MyInteger(null)};
        Random random = new Random();
        for (int length = 0;; length++) {
            for (int attempt = 0; attempt < 100000; attempt++) {
                List<MyInteger> list = new ArrayList<>();
                int[] arr = new int[length];
                for (int k = 0; k < length; k++) {
                    int rand = random.nextInt(3);
                    arr[k] = rand;
                    list.add(array[rand]);
                }
                try {
                    Collections.sort(list, COMPARATOR);
                } catch (Exception e) {
                    System.out.println(arr.length + " " + Arrays.toString(arr));
                    return;
                }
            }
        }
    }
}

问题答案:

它取决于实现,但是在openjdk
8
中,将根据MIN_MERGE(等于32)检查数组的大小。这避免了对mergeLo/
的调用,mergeHi后者会引发异常。

从JDK / jdk / openjdk / 7u40-b43 8-b132 7-b147-8-b132 /
java.util.TimSort

static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
                     T[] work, int workBase, int workLen) {
    assert c != null && a != null && lo >= 0 && lo <= hi && hi <=

a.length;

    int nRemaining  = hi - lo;
    if (nRemaining < 2)
        return;  // Arrays of size 0 and 1 are always sorted

    // 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;
    }

    /**
     * March over the array once, left to right, finding natural runs,
     * extending short natural runs to minRun elements, and merging runs
     * to maintain stack invariant.
     */
    TimSort<T> ts = new TimSort<>(a, c, work, workBase, workLen);
    int minRun = minRunLength(nRemaining);
    do {
        // Identify next run
        int runLen = countRunAndMakeAscending(a, lo, hi, c);

        // If run is short, extend to min(minRun, nRemaining)
        if (runLen < minRun) {
            int force = nRemaining <= minRun ? nRemaining : minRun;
            binarySort(a, lo, lo + force, lo + runLen, c);
            runLen = force;
        }

        // Push run onto pending-run stack, and maybe merge
        ts.pushRun(lo, runLen);
        ts.mergeCollapse();

        // Advance to find next run
        lo += runLen;
        nRemaining -= runLen;
    } while (nRemaining != 0);

    // Merge all remaining runs to complete sort
    assert lo == hi;
    ts.mergeForceCollapse();
    assert ts.stackSize == 1;
}


 类似资料:
  • 以下程序引发以下异常: 我理解的问题。请参阅无法复制:“比较方法违反了其总合同! 我不明白为什么它只对大小为32或更大的s失败。有人能解释吗?

  • 问题内容: 在应使用相同的列表时,我看到一些不一致之处。(Python 2.7.5) 有人有一个简单的解释吗? 问题答案: 使用列表文字,VM会创建具有设置长度的列表。当将序列传递给构造函数时,元素将被一个接一个地添加(通过),因此在适当时调整了列表的大小。由于调整大小操作是为了分摊成本而进行的,因此最终列表通常会比源列表大。

  • 问题内容: 我正在学习Java 8文档。我知道最大数组大小定义为均值2 ^ 31 – 8 = 2147483639 。然后,我集中讨论了为什么要减去8 或减去? 有些人根据文档给出了一些逻辑。因此,对于标题字,减去8。但是在这种情况下,如果标题字需要大于8,那么答案是什么? 请在此基础上澄清我。预先感谢您的合作。 问题答案: 阅读上述有关Java内存管理的文章,其中清楚指出 我认为这适用于Arra

  • 问题内容: 限制Java JVM上Permgen空间大小的目的是什么?为什么不总是将其设置为等于最大堆大小?Java为什么默认为这么少的64MB?他们是否正在试图通过这种方式迫使人们注意代码中的Permgen问题? 如果我的应用使用85MB的permgen,那么将其设置为96MB可能是安全的,但是如果它只是主堆的一部分,为什么还要设置得如此之小呢?允许JVM使用堆允许的PermGen效率不高吗?

  • 我在一个项目中使用Spring/JPA,我有一个实体在列表上有@onetomany注释,另一个实体有@manytoone注释。尽管当我通过父实体的id检索父实体时,返回的列表(子实体)的大小始终为0 测试的文本版本 此父实体 如有任何帮助,不胜感激,谢谢!

  • 问题内容: 我了解,当您添加/删除组件时,需要然后单击。但是,我正在更改多边形的状态。最初显示图像,但是当我按左右键时,图像不会移动。如果我移动窗口,则gui将更新。为什么当我按下按键时它没有更新? 问题答案: 在听众之后打电话。 另外,我建议使用KeyBindings而不是