`

小心使用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  
我要成为第一个赞的人!呵呵,

相关推荐

    ArrayList和LinkedList区别及使用场景代码解析

    ArrayList和LinkedList区别及使用场景代码解析 ArrayList和LinkedList都是Java集合框架中的重要成员,它们都是List接口的实现类,但它们在实现机制、性能和使用场景等方面存在着很大的差异。 ArrayList ...

    为什么阿里要慎重使用ArrayList中的subList方法

    《阿里巴巴Java开发手册》中提到要慎重使用ArrayList的subList方法,这主要是因为该方法返回的是一个视图,而非...在处理并发和性能敏感的代码时,使用`subList`需特别小心,以防止出现意外的程序行为或运行时异常。

    面试必问Java面试题,对标初级Java

    1.Java泛型(约束类型) 2.Java中函数式编程-Stream流-Lambda表达式 3.JVM虚拟机 4.Springcloud里面的组件 5.说一下你对分布式理解是什么样的?...19.ArrayList和LinkedList有什么区别 20.Java面向对象有哪些特征

    JAVA中常用的数据结构

    ArrayList类和Vector类类似,但是ArrayList类不是线程同步的(sychronized),因此在多线程环境下需要小心使用。 Map接口 Map接口是JAVA中另一个常用的数据结构,它提供了键值对的存储和操作方法。Map接口的实现类有...

    突破程序员基本功的16课.part2

    3.3.3 ArrayList和LinkedList的性能分析和适用场景 3.4 Iterator迭代器 迭代时删除指定元素 3.5 小结 第4课 Java的内存回收 4.1 Java引用的种类 4.1.1 对象在内存中状态 4.1.2 强引用 4.1.3 软引用 4.1.4 ...

    java解惑(源代码+教程)

    4. **数组与集合**:Java提供了多种数据结构,如数组、ArrayList和LinkedList等。它们各有优缺点,选择合适的数据结构能大幅提升代码效率。 5. **多线程与并发**:Java提供了丰富的多线程支持,如Thread类、...

    Java面试笔试题(附带详细答案).zip

    1. 集合接口:ArrayList、LinkedList、HashSet、HashMap等的特性和使用场景。 2. 泛型:理解泛型的作用,如何使用泛型方法和泛型类。 3. 接口与抽象类:接口的定义、实现与多态,抽象类的使用。 4. 序列化:理解序列...

    java解惑(java谜题)中文版的

    6. **数组与集合**:理解数组和集合(如ArrayList、LinkedList)之间的性能差异,以及何时使用它们,是避免谜题的关键。 7. **运算符优先级**:不熟悉运算符优先级可能导致计算结果不符合预期,如赋值运算符的特殊...

    基于java开发的图书管管理系统(视频+源码).zip

    同时,Java集合框架(如ArrayList、LinkedList、HashMap等)在处理数据结构时起着关键作用。 2. **MVC设计模式**:图书管理系统可能采用了Model-View-Controller(MVC)架构,这是一种将业务逻辑、用户界面和数据...

    Java2核心技术第6版卷1基础知识.rar

    10. **集合框架**:介绍ArrayList、LinkedList、Vector、HashSet、HashMap等基本集合类,以及泛型、迭代器和集合接口(如List、Set、Map)的使用。 11. **多线程**:讲解并发编程的概念,如何创建和管理线程,以及...

    java编码规范考试题答案.docx

    1. **集合类**:集合类在Java中是数据存储的重要工具,如ArrayList、LinkedList、HashSet等。规范指出,集合类的命名应当具有复数含义,例如users或products。集合中的对象在不再使用时,由垃圾回收器自动回收,不...

    java程序中容易出错的地方

    - **ArrayList vs LinkedList**:对于频繁插入删除的场景,应该选择`LinkedList`;而如果主要是元素的读取操作,则`ArrayList`更合适。 - **空指针异常**:在向集合中添加元素之前,务必检查该元素是否为null,以...

    Java笔试题(附带详细答案).7z

    - List、Set、Map接口:熟悉ArrayList、LinkedList、HashSet、HashMap等具体实现类的特性,以及它们之间的区别。 - 遍历和迭代器:理解并熟练运用foreach循环和Iterator遍历集合。 - 泛型:掌握泛型的基本用法,...

    Java编写中容易搞错的一些东西.rar

    而集合(如ArrayList、LinkedList等)长度可变,提供了更多的操作方法,如添加、删除元素。在选择使用时需根据实际需求进行判断。 6. **同步与并发**:Java中的synchronized关键字用于控制多线程环境下的并发访问,...

    AlgorithmsNotes:阅读算法第4版时的注意事项

    在Java中,容器类库(如ArrayList、LinkedList、HashMap和TreeMap)是实现这些数据结构的工具,理解它们的内部实现机制能帮助你更好地优化代码。例如,ArrayList适合于随机访问,而LinkedList适合于频繁插入和删除。...

    java面试题-集合类

    由 Vector 创建的 Iterator,虽然和 ArrayList 创建的 Iterator 是同一接口,但是,Vector 是同步的,当一个 Iterator 被创建 正在被使用,另一个线程改变了 Vector 的状态(例如,添加或删除了一些元素),这时调用...

    Assignment_Code:我的作业中的代码

    6. **集合框架**:ArrayList、LinkedList、HashMap、HashSet等集合类的使用。 7. **IO流与NIO**:基础IO流操作,以及非阻塞I/O(New IO)的使用。 8. **多线程**:线程的创建、同步、协作以及线程池的使用。 9. **...

    rust_linked_lists

    在Rust中,由于其所有权和生命周期的概念,链表的实现与传统的动态数组(如C++或Java中的ArrayList)有所不同。 Rust 的标准库并没有内置链表类型,但我们可以使用迭代器和指针来创建自定义的链表结构。Rust 提供了...

Global site tag (gtag.js) - Google Analytics