小心使用ArrayList和LinkedList

来源:互联网 发布:新疆人 知乎 编辑:程序博客网 时间:2024/04/30 05:13

转载自 : http://www.exceptionhelp.com/javadetail?articleId=551


最近在看数据结构,发现这个东西不错,记录下来,以便分享!  
ArrayList内部是使用可増长数组实现的,所以是用get和set方法是花费常数时间的,但是如果插入元素和删除元素,除非插入和删除的位置都在表末尾,否则代码开销会很大,因为里面需要数组的移动。
LinkedList是使用双链表实现的,所以get会非常消耗资源,除非位置离头部很近。但是插入和删除元素花费常数时间。
我们来看下面一个例子:
public void listTest1(List<Integer> list, int n) {
list.clear();
for (int i=0; i<n; i++) {
list.add(i);
}
}
上面这个例子,不论是ArrayList还是LinkedList运行的时间都是O(N),因为每次的操作都是在表末尾进行的。如果我们把add方法换成下面的呢!
list.add(0, i);
该完之后,对于LinkedList运行时间是O(N),但是对于ArrayList则是O(N^2),一位每次添加都需要移动数组,运行时间是O(N)
再看看下面的这个例子:计算list中数据之和
public int listSum(List<Integer> list) {
int sum = 0;
for (int i=0; i<list.size(); i++) {
sum += list.get(i);
}
return sum;
}
这个例子对于ArrayList运行时间是O(N),但是对于LinkedList运行时间就是O(N^2),但是如果我们使用增强for循环,那么就会不一样。代码如下:
public int listSum2(List<Integer> list) {
int sum = 0;
for (Integer i: list) {
sum += i;
}
return sum;
}
这样无论是ArrayList和LinkedList其运行时间都是O(N),因为迭代器会有效的从一项到下一项推进。
下面我们说一个remove方法
问题:将一个表中的所有偶数项全部删除,要求不能创建新表。
最简单的就是,进行for遍历,然后取出数值判断是否是偶数,如果是就删除,代码如下:
public static void removeEvensOne(List<Integer> list) {
for (int i=0; i<list.size(); i++) {
if (list.get(i) % 2 == 0) {
list.remove(i); //移除的是i位置上的元素
}
}
}
我们来分析一下,如果传入的参数是Arraylist,那么效率一定会很低,我做了一下测试,创建一个400000项的ArrayList,程序运行时间大约是16s。想着如果传入的是LinkedList是否会很快,答案是否!对于LinkedList运行时间至少要一分多钟,这就奇怪了LinkedList不是删除操作是常数时间吗。我们仔细看看这里面需最消耗时间的是list.get(i)和list.remove(i),首先对于LinkedList的get()操作时间复杂度是O(N),对于remove(i)时间,LinkedList需要到达位置i,那么如何到达,只能是从第一个元素一直搜索下去,其时间复杂读也是O(N),所以对于这个程序LinkedList消耗的时间要比ArrayList多的多。
既然用for循环需要.get(i),那我们使用增强for循环好了,代码如下:
public static void removeEvensThree(List<Integer> list) {
for (Integer i: list) {
if (i%2 == 0) {
list.remove(i); //移除的是元素i
}
}
}
运行一下,抛出异常java.util.ConcurrentModificationException。也就是说list.remove()破坏了增强for循环,这个方法不行,但是它给了我们一个思路:在迭代器找到一个偶数时,我们可以使用迭代器来删除这个偶数值。我们使用Iterator迭代器,代码如下:
public static void removeEvensTwo(List<Integer> list) {
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
if (it.next() % 2 == 0) {
it.remove();
}
}
}
这个代码中我们不需要查找,还是使用400000个元素的list进行测试,ArrayList没有什么变化,时间依然是16s,这是因为即使迭代器位于要删除的节点上,删除之后元素还是要移动。对于LinkedList花费的时间小于1s。
从上面我们可以看出:ArrayList虽然get和set快,但是remove却很慢。对于LinkedList虽然删除和插入很快,但是如果期间使用了get那么一样会很慢。如果是有大量的删除操作我们还是使用Iterator迭代器比较好。

0 0
原创粉丝点击