java arrayList remove(元素)的时间复杂性
我试图绘制ArrayList的remove(element)方法的时间复杂度。 我的理解是它应该是O(N),然而,它给了我一个O(1)。有人能指出我做错了什么吗?? 先谢谢你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
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;