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迭代器比较好。
相关推荐
ArrayList和LinkedList区别及使用场景代码解析 ArrayList和LinkedList都是Java集合框架中的重要成员,它们都是List接口的实现类,但它们在实现机制、性能和使用场景等方面存在着很大的差异。 ArrayList ...
《阿里巴巴Java开发手册》中提到要慎重使用ArrayList的subList方法,这主要是因为该方法返回的是一个视图,而非...在处理并发和性能敏感的代码时,使用`subList`需特别小心,以防止出现意外的程序行为或运行时异常。
1.Java泛型(约束类型) 2.Java中函数式编程-Stream流-Lambda表达式 3.JVM虚拟机 4.Springcloud里面的组件 5.说一下你对分布式理解是什么样的?...19.ArrayList和LinkedList有什么区别 20.Java面向对象有哪些特征
ArrayList类和Vector类类似,但是ArrayList类不是线程同步的(sychronized),因此在多线程环境下需要小心使用。 Map接口 Map接口是JAVA中另一个常用的数据结构,它提供了键值对的存储和操作方法。Map接口的实现类有...
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 ...
4. **数组与集合**:Java提供了多种数据结构,如数组、ArrayList和LinkedList等。它们各有优缺点,选择合适的数据结构能大幅提升代码效率。 5. **多线程与并发**:Java提供了丰富的多线程支持,如Thread类、...
1. 集合接口:ArrayList、LinkedList、HashSet、HashMap等的特性和使用场景。 2. 泛型:理解泛型的作用,如何使用泛型方法和泛型类。 3. 接口与抽象类:接口的定义、实现与多态,抽象类的使用。 4. 序列化:理解序列...
6. **数组与集合**:理解数组和集合(如ArrayList、LinkedList)之间的性能差异,以及何时使用它们,是避免谜题的关键。 7. **运算符优先级**:不熟悉运算符优先级可能导致计算结果不符合预期,如赋值运算符的特殊...
同时,Java集合框架(如ArrayList、LinkedList、HashMap等)在处理数据结构时起着关键作用。 2. **MVC设计模式**:图书管理系统可能采用了Model-View-Controller(MVC)架构,这是一种将业务逻辑、用户界面和数据...
10. **集合框架**:介绍ArrayList、LinkedList、Vector、HashSet、HashMap等基本集合类,以及泛型、迭代器和集合接口(如List、Set、Map)的使用。 11. **多线程**:讲解并发编程的概念,如何创建和管理线程,以及...
1. **集合类**:集合类在Java中是数据存储的重要工具,如ArrayList、LinkedList、HashSet等。规范指出,集合类的命名应当具有复数含义,例如users或products。集合中的对象在不再使用时,由垃圾回收器自动回收,不...
- **ArrayList vs LinkedList**:对于频繁插入删除的场景,应该选择`LinkedList`;而如果主要是元素的读取操作,则`ArrayList`更合适。 - **空指针异常**:在向集合中添加元素之前,务必检查该元素是否为null,以...
- List、Set、Map接口:熟悉ArrayList、LinkedList、HashSet、HashMap等具体实现类的特性,以及它们之间的区别。 - 遍历和迭代器:理解并熟练运用foreach循环和Iterator遍历集合。 - 泛型:掌握泛型的基本用法,...
而集合(如ArrayList、LinkedList等)长度可变,提供了更多的操作方法,如添加、删除元素。在选择使用时需根据实际需求进行判断。 6. **同步与并发**:Java中的synchronized关键字用于控制多线程环境下的并发访问,...
在Java中,容器类库(如ArrayList、LinkedList、HashMap和TreeMap)是实现这些数据结构的工具,理解它们的内部实现机制能帮助你更好地优化代码。例如,ArrayList适合于随机访问,而LinkedList适合于频繁插入和删除。...
由 Vector 创建的 Iterator,虽然和 ArrayList 创建的 Iterator 是同一接口,但是,Vector 是同步的,当一个 Iterator 被创建 正在被使用,另一个线程改变了 Vector 的状态(例如,添加或删除了一些元素),这时调用...
6. **集合框架**:ArrayList、LinkedList、HashMap、HashSet等集合类的使用。 7. **IO流与NIO**:基础IO流操作,以及非阻塞I/O(New IO)的使用。 8. **多线程**:线程的创建、同步、协作以及线程池的使用。 9. **...
在Rust中,由于其所有权和生命周期的概念,链表的实现与传统的动态数组(如C++或Java中的ArrayList)有所不同。 Rust 的标准库并没有内置链表类型,但我们可以使用迭代器和指针来创建自定义的链表结构。Rust 提供了...