繁体   English   中英

java arrayList remove(element) 的时间复杂度

[英]time complexity for java arrayList remove(element)

我试图绘制 ArrayList 的 remove(element) 方法的时间复杂度。 我的理解是它应该是 O(N),但是,它给了我一个 O(1)。 谁能指出我在这里做错了什么? 先感谢您。

   public static void arrayListRemoveTiming(){
    long startTime, midPointTime, stopTime;
    // Spin the computer until one second has gone by, this allows this
    // thread to stabilize;
    startTime = System.nanoTime();
    while (System.nanoTime() - startTime < 1000000000) {
    }
    long timesToLoop = 100000;
    int N;
    ArrayList<Integer> list = new ArrayList<Integer>();

    // Fill up the array with 0 to 10000
    for (N = 0; N < timesToLoop; N++)
        list.add(N);

    startTime = System.nanoTime();
    for (int i = 0; i < list.size() ; i++) {
        list.remove(i);

        midPointTime = System.nanoTime();
        // Run an Loop to capture other cost.
        for (int j = 0; j < timesToLoop; j++) {

        }
        stopTime = System.nanoTime();

        // Compute the time, subtract the cost of running the loop
        // from the cost of running the loop.

        double averageTime = ((midPointTime - startTime) - (stopTime - midPointTime))
                / timesToLoop;

        System.out.println(averageTime);
    }


}

remove的成本是O(n) ,因为您必须将“左”点的“右”的元素洗牌一个:

                 Delete D
                     |
                     V
+-----+-----+-----+-----+-----+-----+-----+
|  A  |  B  |  C  |  D  |  E  |  F  |  G  |
+-----+-----+-----+-----+-----+-----+-----+
                     <------------------
                      Move E, F, G left

如果你的测试代码给你O(1)那么我怀疑你没有正确测量它:-)

例如,OpenJDK 源代码有:

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

System.arraycopy是这个函数的O(n)成本。


此外,我不确定您是否考虑得很好:

for (int i = 0; i < list.size() ; i++)
    list.remove(i);

这将从原始列表中删除以下元素:

    0, 2, 4, 8

依此类推,因为删除元素0的操作会将所有其他元素向左移动 - 当您删除原始偏移量 0 时,最初位于偏移量 1 的项目将位于偏移量 0,然后您继续删除偏移量 1。

首先,您没有测量此代码的复杂性。 您正在做的是衡量(或试图衡量)绩效。 当您绘制数字(假设它们被正确测量)时,您会在缩放变量的有限值范围内获得特定用例的性能曲线。

这与计算复杂度度量不同; 即大 O,或相关的 Bachman-Landau 符号 这些是关于数学限制的,因为缩放变量趋于无穷大。

这不仅仅是挑剔。 很容易构造示例1 ,其中性能特征随着 N 变得非常大而显着变化。

当您在一系列值上绘制性能并拟合曲线时,所做的是估计复杂性。

1 - 一个真实的例子是各种HashMap函数的平均复杂度,当N达到2^31时,这些函数从O(1)切换到O(N) (具有非常小的C )。 模态是因为哈希数组不能超过2^31个槽。


第二点是ArrayList.remove(index)的复杂度对index的值和列表长度很敏感。

  • 平均和最坏情况下O(N)的“广告”复杂度。

  • 在最好的情况下,复杂度实际上是O(1) 真的!

    当您删除列表的最后一个元素时会发生这种情况; index == list.size() - 1 这可以通过零复制来执行; 查看@paxdiablo 在他的答案中包含的代码。


现在回答你的问题。 您的代码可能给出不正确的测量值的原因有很多。 例如:

  • 您没有考虑 JIT 编译开销和其他 JVM 预热影响。

  • 我可以看到 JIT 编译器可能优化整个循环的地方。

  • 你测量时间的方式很奇怪。 尝试将其视为代数。

     ((midPoint - start) - (stop - midPoint)) / count;

    现在简化......并且midPoint项取消了。

  • 您只从列表中删除了一半的元素,因此您只测量了 50,000 到 100,000 范围内的缩放变量。 (我希望您随后针对缩放变量进行绘图;即您正在针对 N 绘制 f(N + 5000)。

  • 您测量的时间间隔对于您机器上的时钟分辨率来说可能太小了。 (阅读nanoTime()的 javadocs 以查看它保证的分辨率。)

我建议想要避免上述错误的人应该阅读:

remove(int)删除第 i 个 INDEX 处的元素,即 O(1)

你可能想要remove( Object )这是 O(N) 你需要调用remove(Integer.valueOf(i))

如果您的列表中没有按顺序排列的元素,那会更明显

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM