ArrayList 和 LinkedList是两个Collections类,用于存储对象引用列表。举例说,你可以创建Strings类的ArrayList,或者Integers类的LinkedList.这篇文章会比较ArrayList和LinkedList他们的性能,以及提供一些在特定的情况下如何使用正确它们的选择建议。
第一要点是ArrayList返回的是原始的Object数组。正因如此,ArrayList对于随机访问比LinkedList要快许多,就是说,当访问任意的列表元素可以使用get()方法。注意,get()方法也应用在LinkedLists上,但是它需要在列表的头到尾顺序扫描。这种扫描是非常低效的,对于一个LinkedList,没有更快的方法去访问列表中的第N个元素。
思考下面的例子,假如你有一组庞大的分类元素要使用ArrayList或者LinkedList,你也要在这个列表中使用折半查找法。标准的折半查找算法靠一个数值在列表的中间开始检查这个搜索的数值,如果中间值太高,列表中后半部就会被淘汰。然而,如果中间值太低,前半部会被忽略。这个过程会一直继续直到这个值在列表中被找到,或者直到搜索的下界变得大于上界。
这里的程序是在ArrayList或者LinkedList列表中元素使用折半查找法:
[code]
import java.util.*;
public class ListDemo1 {
static final int N = 10000;
static List values;
// make List of increasing Integer values
static {
Integer vals[] = new Integer[N];
Random rn = new Random();
for (int i = 0, currval = 0; i < N; i++) {
vals = new Integer(currval);
currval += rn.nextInt(100) + 1;
}
values = Arrays.asList(vals);
}
// iterate across a list and look up every
// value in the list using binary search
static long timeList(List lst) {
long start = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
// look up a value in the list
// using binary search
int indx = Collections.binarySearch(
lst, values.get(i));
// sanity check for result
// of binary search
if (indx != i) {
System.out.println("*** error ***/n");
}
}
return System.currentTimeMillis() - start;
}
public static void main(String args[]) {
// do lookups in an ArrayList
System.out.println("time for ArrayList = " +
timeList(new ArrayList(values)));
// do lookups in a LinkedList
System.out.println(
"time for LinkedList = " +
timeList(new LinkedList(values)));
}
}
[/code]
ListDemo1类创建了一个分类的Integer数值列表。然后向ArrayList或者LinkedList添加了数值。Collections.binarySearch()方法用来查寻每个在列表中的值。
当你运行这个程序,你应该能看到下面的结果:
time for ArrayList = 31
time for LinkedList = 4640
ArrayList速度比LinkedList快了150倍左右。(你的结果可能与这里不一样,这取决于你机器的特性,但是你应该看到ArrayList的与LinkedList比较结果的明显的区别。对于其它程序用这种方法也是同样的。)很明显,在这种情形下用LinkedList是一种坏的选择。折半查找算法天生使用随机访问,而LinkedList不支持快速随机访问。LinkedList使用随机访问的时间与列表大小成正比例。比较起来,在ArrayList中使用随机访问有一个比较固定的时间。
你能用RandomAccess接口去检查一个列表是否支持快速随机访问:
void f(List lst) {
if (lst instanceof RandomAccess) {
// supports fast random access
}
}
ArrayList实现了RandomAccess接口,而LinkedList则没有。注意Collections.binarySearch()方法利用RandomAccess的特性使搜索变得完善。
这些结果表明,ArrayList始终是一个更好的选择?未必。也有许多LinkedList做得更好的例子。而且注意在许多情况下,关于LinkedList的算法能实现的更有效。举一个例子,反转LinkedList利用Collections.reverse。内置算法能做这些,并且利用iterators的前移和后移获得更合理的性能,
来看看另一个例子。假如你有一组元素,你向这个列表中插入和删除许多元素。如果这样,LinkedList是比较好的选择。为了证明,考虑下面“worst case”的情形。在这个演示中,程序重复地在列表的开始处插入元素。代码是这样的:
import java.util.*;
public class ListDemo2 {
static final int N = 50000;
// time how long it takes to add
// N objects to a list
static long timeList(List lst) {
long start = System.currentTimeMillis();
Object obj = new Object();
for (int i = 0; i < N; i++) {
lst.add(0, obj);
}
return System.currentTimeMillis() - start;
}
public static void main(String args[]) {
// do timing for ArrayList
System.out.println(
"time for ArrayList = " +
timeList(new ArrayList()));
// do timing for LinkedList
System.out.println(
"time for LinkedList = " +
timeList(new LinkedList()));
}
}
当你运行这个程序,你应该能看到下面的结果:
time for ArrayList = 4859
time for LinkedList = 125
这结果看起来与前一个例子几乎颠倒了。
当在ArrayList的起始处添加一个元素,所有存在的元素必须把自己向后推,这意味着许多珍贵的数据需要复制和移动。相反,在LinkedList起始处添加一个元素只是简单的意味分配一条内部记录给这个元素,然后调整两条链记录。在LinkedList起始添加元素有固定的成本,但是在ArrayList起始处添加元素则成本与列表的大小成正比。
目前为止,这些方法看起来只是速度问题,那关于空间大小怎么样呢?让我来看看ArrayList和LinkedList在Java 2 SDK,Standard Edition v 1.4中实现的内部细节。这些细节并非这些类的外部说明的一部分。但说明了这些类如何在内部工作。
LinkedList类有一个私有的内部类定义:
private static class Entry {
Object element;
Entry next;
Entry previous;
}
每一个Entry对象引用一个列表元素,和previous元素一起在LinkedList中。--换句话说,就是一个双链表。一个有1000个元素的LinkedList就与1000个Entry对象连在一起,参考实际的列表元素。因为这些Entry对象,LinkedList结构中开销了重要的空间。
ArrayList则拥有一个支持对象数组的东西。
使用arraylist和linkedlist
最新推荐文章于 2024-04-29 09:48:26 发布