[英]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.