ArrayList 和 LinkedList 是 Java 中两个常用的集合类,它们都实现了 List
接口,但由于底层实现不同,在实际使用中有很大区别。
1. 底层实现结构
-
ArrayList:
- 底层是一个动态数组(类似于一个可变长度的数组)。
- 元素在内存中是连续存储的,这意味着可以通过索引快速访问元素。
- 如果数组容量不足时,会创建一个更大的数组,复制旧数组内容到新数组中。
-
LinkedList:
- 底层是一个双向链表。
- 每个节点包含两个部分:存储元素的值(数据部分)和指向前后节点的引用(指针部分)。
- 元素在内存中不是连续的,而是通过指针相互连接。
2. 访问性能
-
ArrayList:
- 访问性能非常高,时间复杂度是 O(1)。
- 由于底层是数组,可以通过索引直接定位到元素位置。
-
LinkedList:
- 访问性能较低,时间复杂度是 O(n)。
- 需要从头(或尾)一个节点一个节点地遍历到指定位置。
总结:如果频繁需要随机访问某个位置的元素,ArrayList
更适合。
3. 插入和删除时间复杂度
-
ArrayList:
- 插入或删除元素时,可能需要移动后续的元素(如果不是在数组末尾操作)。
- 时间复杂度为:
- O(n)(在中间插入/删除)。
- O(1)(在末尾插入时通常是常数时间,但当数组扩容时耗时会增加)。
-
LinkedList:
- 插入或删除元素时,不需要移动其他元素,只需更新前后节点的引用。
- 时间复杂度为:
- O(1)(在已知节点的情况下操作,比如头部或尾部)。
- O(n)(如果需要先遍历找到插入/删除的位置)。
总结:如果频繁需要在中间进行插入或删除操作,LinkedList
更适合。
4. 继承关系
-
ArrayList:
- 继承自
AbstractList
。 - 实现了
List
、RandomAccess
、Cloneable
和Serializable
等接口。 - 支持快速随机访问(
RandomAccess
接口是一个标志性接口)。
- 继承自
-
LinkedList:
- 继承自
AbstractSequentialList
。 - 实现了
List
、Deque
、Cloneable
和Serializable
等接口。 - 支持双端队列操作(
Deque
接口支持在头部或尾部操作)。
- 继承自
总结:ArrayList
更加专注于随机访问,而 LinkedList
除了作为列表,还可以充当一个队列或栈。
5. 使用场景对比
-
ArrayList:
- 适合需要频繁随机访问元素的场景,比如索引定位。
- 不适合频繁插入或删除的情况(特别是中间位置的操作)。
-
LinkedList:
- 适合需要频繁插入、删除元素的场景。
- 不适合频繁随机访问的场景。
6. 内存消耗对比
-
ArrayList:
- 由于使用的是数组,存储的仅仅是元素本身,内存开销较小。
- 如果数组扩容时,可能会短时间占用较多内存(旧数组和新数组同时存在)。
-
LinkedList:
- 由于每个节点需要额外存储指针(前后节点的引用),内存开销较大。
总结:如果内存空间非常有限,ArrayList
更加节约内存。
总结对比表
特性 | ArrayList | LinkedList |
---|---|---|
底层结构 | 动态数组 | 双向链表 |
访问性能 | 快(O(1)) | 慢(O(n)) |
插入/删除性能 | 慢(O(n) 中间操作) | 快(O(1) 已知节点插入/删除) |
内存消耗 | 较小 | 较大 |
适用场景 | 随机访问频繁 | 插入、删除操作频繁 |
文字先行,后续补图