java arrayList remove(元素)的时间复杂性

java arrayList remove(元素)的时间复杂性,java,Java,我试图绘制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

我试图绘制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);
    }


}
publicstaticvoid arrayListRemoveTiming(){
长启动时间、中点时间、停止时间;
//旋转计算机直到一秒钟过去,这允许
//线程稳定;
startTime=System.nanoTime();
而(System.nanoTime()-startTime<100000000){
}
长时间toloop=100000;
int N;
ArrayList=新建ArrayList();
//用0到10000填充数组
对于(N=0;N//计算时间,减去运行循环的成本
//从运行循环的成本中。
双平均时间=((中点时间-开始时间)-(停止时间-中点时间))
/时间间隔;
系统输出打印项次(平均时间);
}
}
删除(int)
删除第i个索引处的元素,即O(1)

您可能需要
remove(Object)
这是您需要调用
remove(Integer.valueOf(i))


如果您的列表中没有按顺序排列元素,则更为明显,
删除的成本是
O(n)
,因为您必须将元素在“左”点上方移动一次。如果您的测试代码给出了
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
的操作会向左移动所有其他元素-删除原始偏移量0后,原来位于偏移量1的项目将位于偏移量0,然后继续删除偏移量1

你最好是:

list.remove(0);
在循环内部。

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

这与计算复杂性度量不同;i、 e.大O或相关。这些是关于数学极限,因为缩放变量趋于无穷大

这不仅仅是吹毛求疵。很容易构造示例1,当N变得非常大时,性能特征会发生显著变化

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

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


第二点是,
ArrayList.remove(index)
的复杂性对
index
的值以及列表长度很敏感

  • 对于平均和最坏情况,
    O(N)
    的“广告”复杂性

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

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


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

  • 您没有考虑JIT编译开销和其他JVM预热效应

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

  • 你测量时间的方式很奇怪。试着把它当作代数

            ((midPoint - start) - (stop - midPoint)) / count;
    
    现在简化。。。而
    中点
    项将被抵消

  • 您只从列表中删除了一半的元素,因此您只能测量50000到100000范围内的缩放变量。(我想你是在用缩放变量作图,也就是说,你是在用f(N+5000)和N作图

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

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


从理论上讲,移除数组中的对象是O(n),即使使用随机访问(索引)移除仅为O(1),O(n)来自重新排列的部分,其中项目被移动以替换该项目。“从运行循环的成本中减去运行循环的成本”?这赢得了有史以来最无用的评论奖:-)空循环应该做什么?始终绘制时间度量。这确实是分析代码性能的一项重要技术。O(N)与O(1)是一个不同的世界,并且确实指向了一个无效的实现——如果您在一个不重要的大小范围内测量它,并且看到图形清楚地遵循这个规律(即,在日志图上是直线)。
remove(int)
不是
O(1)
。它是
O(n)
。如果除去最后一个元素以外的任何元素,则必须
        ((midPoint - start) - (stop - midPoint)) / count;