我希望更多地了解数据结构及其实现(使用JAVA语言)。今天我写了一个测试来比较(时间比较)ADT列表的不同实现。具体来说,我比较了add方法,这是我的测试:
@Test
public void testTime() {
long i = 10000000;
long INITIALIZED_VALUE=5000000;
List<Integer> arrayBasedList = new ArrayList<Integer>();
List<Integer> linkedBasedList = new LinkedList<Integer>();
List<Integer> arrayBasedInitialedSizeList = new ArrayList<Integer> (INITIALIZED_VALUE);
long t1 = System.currentTimeMillis();
for (int index = 0; index <= i; index++) {
arrayBasedList.add(index);
}
long t1End = System.currentTimeMillis() - t1;
long t2 = System.currentTimeMillis();
for (int index = 0; index <= i; index++) {
linkedBasedList.add(index);
}
long t2End = System.currentTimeMillis() - t2;
long t3 = System.currentTimeMillis();
for (int index = 0; index <= i; index++) {
arrayBasedInitialedSizeList.add(index);
}
long t3End = System.currentTimeMillis() - t3;
System.out.println("ArrayBased: " + t1End);
System.out.println("LinkedList:" + t2End);
System.out.println("ArrayBasedInitializedSize: " + t3End);
System.out.println("End");
}
我得到了这个结果:
ArrayBased:5681 LinkedList:12830 ArrayBasedInitializedSize:858
为什么LinkedList比ArrayList实现慢?我认为对于LinkedList实现,add方法比为Array实现添加方法更快。
任何人都可以解释为什么数组比addlist的linkedlist更快?
由于
Alessio的
答案 0 :(得分:4)
这里有两件事。
首先是你的时间不可靠,因为你没有在每次运行之前“预热”代码。 Java在运行时优化和编译代码,因此通过它的前几次比以后运行要慢得多。你应该通过你的测试循环几百次,扔掉那些结果,然后做时间。
回答这个问题:
无论列表有多长, LinkedList
都是固定时间,但每次添加它都需要做更多的工作。它需要创建包装器对象,将其插入列表,更新引用等。
另一方面,ArrayList
只是在数组中设置一个值,然后递增大小计数器。只有当它需要重新分配数组时才需要做很多工作。
测试在开始时添加新对象并比较结果,然后你会发现事情更多地回到链接列表中,因为现在ArrayList
需要每次都重新调整所有值。
重新分配数组的成本也通过第三次测试来说明,通过事先了解大小,ArrayList变得更加高效。
在讨论算法时,Big O表示法很有用,但您需要了解它实际意味着什么,或者它可能会产生误导。它谈论的是操作顺序,而不是它实际需要多长时间。对于大多数追加插入,ArrayList
和LinkedList
都是O(1),如果需要重新分配,ArrayList
插入是O(n)。
所有O(1)都表示,无论列表中有多少个对象,它仍然需要相同的时间。在10个项目列表的末尾添加一些内容,100个项目列表,10000个项目列表,它仍然需要相同的时间。
LinkedList虽然比ArrayList花费的时间更多 - 即使它们仍然具有相同的O(1)顺序。
实际上速度差异是这样的,即使你看到列表的开头添加了LinkedList是O(1)而ArrayList是O(n),对于小型列表,ArrayList仍然更快!
为了给出一些想法(这只是一个例子,不要试图从中获取精确的时间!)然后如果为LinkedList添加的时间是L并且ArrayList的时间是A则那么总数最后添加的时间是L * 1,A * 1。要在开始时添加L * 1,A * N。
所以如果L / A&lt; N然后使用ArrayList实际上更快,即使只是查看O特性,您可能认为使用LinkedList会更好。
(如果你需要随机访问读取,链接列表也是O(n),这也是一个很重要的因素。)
答案 1 :(得分:1)
简而言之:在ArrayList上添加操作比在LinkedList上更快,因为它涉及(读取,写入,更新)更少的内存位置:
ArrayList:更新int size
字段,阅读Object[] elements
字段,阅读elements.length
,在索引处写入elements
。
LinkedList:更新int size
字段,更新Entry tail
字段,分配新Entry
,写入新条目的prev
和element
字段,写tail.next
字段。
更精确的时间安排:
@GenerateMicroBenchmark
public int arrayList_add() {
List<Object> list = new ArrayList<>(1000);
Object x = new Object();
for (int i = 0; i < 1000; i++) {
list.add(x);
}
return list.size();
}
@GenerateMicroBenchmark
public int linkedList_add() {
List<Object> list = new LinkedList<>();
Object x = new Object();
for (int i = 0; i < 1000; i++) {
list.add(x);
}
return list.size();
结果:
Benchmark Mean Mean error Units
arrayList_add 5,234 0,019 usec/op
linkedList_add 6,417 0,032 usec/op
答案 2 :(得分:0)
我对您的结果感到惊讶,并试图自己运行您的代码。这是我的输出:
ArrayBased: 5589
LinkedList:1219
ArrayBasedInitializedSize: 789
End
我认为,LinkedList的add方法应该比ArrayList慢一点(两个都是O(1),但具有不同的常量)。它们都创建新的Integer对象,而ArrayList只是将它们放在数组中,而LinkedList则创建链接。
ArrayBased和ArrayBasedInitializedSize之间的区别很明显:数组重新分配和元素复制需要花费太多时间。