Java中的LinkedList和ArrayList都是List接口的经典实现,两者的核心差异源于底层存储结构的不同,LinkedList基于双向链表实现,ArrayList基于动态数组实现,这种底层差异直接决定了它们的适用场景和性能表现。

底层存储结构差异
ArrayList的底层是一个可以动态扩容的Object数组,当元素数量超过数组容量时,会创建一个新的更大的数组,将原有元素拷贝过去。而LinkedList的底层是一个双向链表,每个节点包含前驱指针、后继指针和存储的元素值,节点之间通过这些指针关联,不需要连续的内存空间。
我们可以通过查看两者的部分源码来理解底层结构:
// ArrayList核心存储结构
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 存储元素的数组
transient Object[] elementData;
// 元素数量
private int size;
}
// LinkedList核心节点结构
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
// 链表头节点
transient Node<E> first;
// 链表尾节点
transient Node<E> last;
// 链表节点定义
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
核心操作性能对比
查询操作
ArrayList支持随机访问,因为底层是数组,可以通过下标直接定位元素,时间复杂度为O(1)。而LinkedList查询元素时需要从头节点或者尾节点开始遍历,直到找到对应位置的元素,时间复杂度为O(n)。
以下是查询操作的示例对比:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class QueryTest {
public static void main(String[] args) {
List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();
// 初始化10000个元素
for (int i = 0; i < 10000; i++) {
arrayList.add(i);
linkedList.add(i);
}
// ArrayList随机查询
long start1 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
int val = arrayList.get(i);
}
long end1 = System.currentTimeMillis();
System.out.println("ArrayList查询耗时:" + (end1 - start1) + "ms");
// LinkedList随机查询
long start2 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
int val = linkedList.get(i);
}
long end2 = System.currentTimeMillis();
System.out.println("LinkedList查询耗时:" + (end2 - start2) + "ms");
}
}
运行上述代码会发现,ArrayList的查询耗时远低于LinkedList,当数据量越大时,差距越明显。
增删操作
如果是尾部的增删操作,ArrayList如果不需要扩容的话是O(1),需要扩容时会有数组拷贝的开销;LinkedList尾部增删只需要修改尾节点指针,也是O(1)。但如果是中间位置的增删,ArrayList需要移动对应位置之后的所有元素,时间复杂度为O(n);而LinkedList只需要找到对应节点修改前后指针即可,时间复杂度为O(1),不过查找节点的过程是O(n),所以实际中间增删时LinkedList的性能优势在数据量较大时才明显。
中间插入操作的示例如下:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class InsertTest {
public static void main(String[] args) {
List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();
// 初始化10000个元素
for (int i = 0; i < 10000; i++) {
arrayList.add(i);
linkedList.add(i);
}
// ArrayList中间插入
long start1 = System.currentTimeMillis();
arrayList.add(5000, 999);
long end1 = System.currentTimeMillis();
System.out.println("ArrayList中间插入耗时:" + (end1 - start1) + "ms");
// LinkedList中间插入
long start2 = System.currentTimeMillis();
linkedList.add(5000, 999);
long end2 = System.currentTimeMillis();
System.out.println("LinkedList中间插入耗时:" + (end2 - start2) + "ms");
}
}
内存占用对比
ArrayList的内存占用主要是数组本身和元素,数组会有一定的容量冗余,也就是预留一些空位减少扩容次数。而LinkedList的每个节点都需要存储前驱指针、后继指针和元素值,每个节点的额外开销比ArrayList的数组元素更大,所以当存储相同数量的元素时,LinkedList的内存占用通常更高。
适用场景总结
根据两者的特性,我们可以总结出适用场景:
- 如果需要频繁进行查询操作,或者数据量不大,优先选择ArrayList
- 如果需要频繁在中间位置进行增删操作,且数据量较大,优先选择LinkedList
- 如果需要实现队列、栈等数据结构,LinkedList实现了Deque接口,更适合这类场景
- 如果内存资源比较紧张,ArrayList的内存利用率更高
| 对比维度 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 查询性能 | O(1),支持随机访问 | O(n),需要遍历 |
| 中间增删性能 | O(n),需要移动元素 | 查找节点O(n),修改指针O(1) |
| 内存占用 | 较低,有数组容量冗余 | 较高,每个节点有额外指针开销 |
| 适用场景 | 查询多、数据量适中 | 中间增删多、实现队列栈结构 |
LinkedListArrayListJava集合双向链表数组修改时间:2026-06-14 13:09:18