-
底层数据结构
-
ArrayList 是动态数组的数据结构实现
-
LinkedList 是双向链表的数据结构实现
-
-
操作数据效率
-
ArrayList按照下标查询的时间复杂度O(1)【内存是连续的,根据寻址公式】, LinkedList不支持下标查询
-
查找(未知索引): ArrayList需要遍历,链表也需要遍历,时间复杂度都是O(n)
-
新增和删除
-
ArrayList尾部插入和删除,时间复杂度是O(1);其他部分增删需要挪动数组,时间复杂度是O(n)
-
LinkedList头尾节点增删时间复杂度是O(1),其他都需要遍历链表,时间复杂度是O(n)
-
-
-
内存空间占用
-
ArrayList底层是数组,内存连续,节省内存
-
LinkedList 是双向链表需要存储数据,和两个指针,更占用内存
-
-
线程安全
-
ArrayList和LinkedList都不是线程安全的
-
如果需要保证线程安全,有两种方案:
-
在方法内使用,局部变量则是线程安全的
-
使用线程安全的ArrayList和LinkedList
-
-
ArrayList 和 LinkedList 是 Java 集合框架中两种常用的列表实现,它们的核心区别在于底层数据结构和适用场景。以下是详细对比:
1. 底层数据结构
类别 | ArrayList | LinkedList |
---|---|---|
数据结构 | 动态数组(基于 Object[] ) | 双向链表(节点包含前驱和后继引用) |
内存布局 | 内存连续,支持快速随机访问 | 内存分散,通过指针连接节点 |
2. 核心操作性能对比
**(1) 访问元素(Get/Set)**
- ArrayList:时间复杂度 O(1)
java
// 直接通过索引访问数组元素 elementData[index]
- LinkedList:时间复杂度 O(n)
java
// 需要从头或尾遍历链表节点 Node<E> node = first; for (int i = 0; i < index; i++) node = node.next;
**(2) 插入/删除元素(Add/Remove)**
- ArrayList:
- 末尾操作:均摊时间复杂度 **O(1)**(可能需要扩容)。
- 中间/头部操作:时间复杂度 **O(n)**(需要移动后续元素)。
java
// 插入到中间时,需要复制数组元素 System.arraycopy(elementData, index, elementData, index + 1, size - index);
- LinkedList:
- 已知节点位置(如头/尾):时间复杂度 O(1)。
- 按索引插入/删除:时间复杂度 **O(n)**(需要先遍历到目标位置)。
java
// 插入到链表中间只需修改相邻节点的引用 Node<E> newNode = new Node<>(prev, element, next); prev.next = newNode; next.prev = newNode;
3. 内存占用
类别 | ArrayList | LinkedList |
---|---|---|
内存开销 | 仅需存储元素和数组容量(可能有预留空间) | 每个元素需要额外存储两个引用(前驱+后继) |
空间浪费 | 动态扩容时可能预留未使用的数组空间 | 无预留空间,但每个节点有固定内存开销 |
4. 适用场景
场景 | ArrayList | LinkedList |
---|---|---|
高频随机访问 | ✅ 适合(直接通过索引访问) | ❌ 不适合(需要遍历链表) |
频繁插入/删除头部 | ❌ 不适合(需移动元素) | ✅ 适合(直接修改头节点引用) |
实现栈/队列 | ✅ 可用(但删除头部效率低) | ✅ 更适合(如 Deque 接口实现) |
内存敏感场景 | ✅ 更优(无节点引用开销) | ❌ 较差(每个节点多两个引用) |
5. 代码示例
**(1) 插入性能对比**
java
// 向列表头部插入10000个元素
List<Integer> arrayList = new ArrayList<>();
List<Integer> linkedList = new LinkedList<>();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
arrayList.add(0, i); // 时间:约 100ms(需频繁移动元素)
}
System.out.println("ArrayList 耗时:" + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
linkedList.addFirst(i); // 时间:约 2ms(仅修改头节点引用)
}
System.out.println("LinkedList 耗时:" + (System.currentTimeMillis() - start));
**(2) 随机访问性能对比**
java
List<Integer> arrayList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> linkedList = new LinkedList<>(Arrays.asList(1, 2, 3, 4, 5));
// ArrayList 访问中间元素
int value = arrayList.get(2); // O(1)
// LinkedList 访问中间元素
int value = linkedList.get(2); // O(n)
6. 其他特性
特性 | ArrayList | LinkedList |
---|---|---|
线程安全 | ❌ 非线程安全(需用 Collections.synchronizedList 或 CopyOnWriteArrayList ) | ❌ 非线程安全(需外部同步) |
实现接口 | 仅实现 List | 实现 List 和 Deque (可当作队列或双端队列) |
总结
-
选择 ArrayList 的场景:
需要频繁随机访问元素,且插入/删除操作多在列表末尾。
典型应用:数据缓存、配置项列表。 -
选择 LinkedList 的场景:
需要频繁在列表头部或中间插入/删除元素,且对随机访问需求较少。
典型应用:实现队列(Queue
)或双端队列(Deque
)。
现代开发建议:大多数情况下优先使用 ArrayList
,仅在需要高效头/尾操作且数据量较大时考虑 LinkedList
。