Java中何时使用LinkedList而非ArrayList

Java中何时使用LinkedList而非ArrayList

技术背景

在Java编程中,ArrayListLinkedListList接口的两种不同实现。ArrayList使用动态数组实现,而LinkedList使用双向链表实现。了解何时选择使用LinkedList而非ArrayList,对于优化代码性能至关重要。

实现步骤

1. 了解两种列表的特点

  • ArrayList
    • 基于动态数组,支持随机访问,通过索引可以在常数时间O(1)内获取元素。
    • 在数组末尾添加元素通常是常数时间O(1),但如果数组容量不足需要扩容,会导致性能下降为O(n)
    • 在数组中间或开头插入、删除元素时,需要移动后续元素,时间复杂度为O(n)
  • LinkedList
    • 基于双向链表,不支持随机访问,获取元素需要从头或尾开始遍历,平均时间复杂度为O(n)
    • 在链表的头部或尾部插入、删除元素的时间复杂度为O(1)
    • 使用迭代器进行插入、删除操作的时间复杂度为O(1)

2. 根据操作类型选择列表

  • 频繁随机访问:如果需要频繁通过索引访问元素,应选择ArrayList。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.ArrayList;
import java.util.List;

public class RandomAccessExample {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
// 随机访问元素
int element = list.get(500);
System.out.println(element);
}
}
  • 频繁插入和删除:如果需要频繁在列表的头部或尾部进行插入、删除操作,或者使用迭代器进行插入、删除操作,应选择LinkedList。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.LinkedList;
import java.util.List;

public class InsertDeleteExample {
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
// 在头部插入元素
list.addFirst(1);
// 在尾部插入元素
list.addLast(2);
// 使用迭代器删除元素
java.util.Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
if (iterator.next() == 1) {
iterator.remove();
}
}
}
}

核心代码

以下是一个简单的测试代码,比较ArrayListLinkedList在不同操作下的性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ArrayListVsLinkedListTest {
public static void main(String[] args) {
int size = 100000;

// 测试ArrayList在末尾添加元素的性能
List<Integer> arrayList = new ArrayList<>();
long startTime = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
arrayList.add(i);
}
long endTime = System.currentTimeMillis();
System.out.println("ArrayList add at end: " + (endTime - startTime) + " ms");

// 测试LinkedList在末尾添加元素的性能
List<Integer> linkedList = new LinkedList<>();
startTime = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
linkedList.add(i);
}
endTime = System.currentTimeMillis();
System.out.println("LinkedList add at end: " + (endTime - startTime) + " ms");

// 测试ArrayList在中间插入元素的性能
startTime = System.currentTimeMillis();
arrayList.add(size / 2, 100001);
endTime = System.currentTimeMillis();
System.out.println("ArrayList add in middle: " + (endTime - startTime) + " ms");

// 测试LinkedList在中间插入元素的性能
startTime = System.currentTimeMillis();
linkedList.add(size / 2, 100001);
endTime = System.currentTimeMillis();
System.out.println("LinkedList add in middle: " + (endTime - startTime) + " ms");
}
}

最佳实践

  • 大多数情况优先选择ArrayList:由于ArrayList支持随机访问,在大多数情况下性能更好,除非你明确知道需要频繁进行插入、删除操作。
  • 提前指定ArrayList的初始容量:如果知道需要存储的元素数量,可以在创建ArrayList时指定初始容量,避免频繁扩容。
1
List<Integer> list = new ArrayList<>(1000);
  • 使用接口类型声明列表:为了提高代码的可维护性和可扩展性,使用List接口类型声明列表,而不是具体的实现类。
1
List<Integer> list = new ArrayList<>();

常见问题

1. LinkedList的插入和删除操作一定比ArrayList快吗?

不一定。虽然LinkedList在理论上插入和删除操作的时间复杂度为O(1),但在实际应用中,由于需要遍历链表找到插入或删除的位置,平均时间复杂度为O(n)。当操作发生在链表的头部或尾部时,LinkedList的性能优势才会体现出来。

2. ArrayList和LinkedList哪个更节省内存?

ArrayList通常更节省内存。LinkedList的每个节点需要额外存储指向前一个和后一个节点的引用,因此会占用更多的内存空间。而ArrayList只需要存储元素本身。

3. 如何在ArrayList和LinkedList之间进行性能测试?

可以使用System.currentTimeMillis()System.nanoTime()记录操作的开始和结束时间,计算操作的耗时。同时,为了减少测试结果的误差,可以多次运行测试并取平均值。


Java中何时使用LinkedList而非ArrayList
https://119291.xyz/posts/java-when-to-use-linkedlist-over-arraylist/
作者
ww
发布于
2025年4月23日
许可协议