`

小心使用ArrayList和LinkedList

    博客分类:
  • Java
阅读更多

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迭代器比较好。

分享到:
评论
2 楼 SpringJava 2014-07-21  
摘过来的
1 楼 jingjing0907 2014-07-18  
我要成为第一个赞的人!呵呵,

相关推荐

Global site tag (gtag.js) - Google Analytics