ArrayList和LinkedList的区别是什么?

  • 底层数据结构

    • 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. 底层数据结构

类别ArrayListLinkedList
数据结构动态数组(基于 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. 内存占用

类别ArrayListLinkedList
内存开销仅需存储元素和数组容量(可能有预留空间)每个元素需要额外存储两个引用(前驱+后继)
空间浪费动态扩容时可能预留未使用的数组空间无预留空间,但每个节点有固定内存开销

4. 适用场景

场景ArrayListLinkedList
高频随机访问✅ 适合(直接通过索引访问)❌ 不适合(需要遍历链表)
频繁插入/删除头部❌ 不适合(需移动元素)✅ 适合(直接修改头节点引用)
实现栈/队列✅ 可用(但删除头部效率低)✅ 更适合(如 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. 其他特性

特性ArrayListLinkedList
线程安全❌ 非线程安全(需用 Collections.synchronizedList 或 CopyOnWriteArrayList❌ 非线程安全(需外部同步)
实现接口仅实现 List实现 List 和 Deque(可当作队列或双端队列)

总结

  • 选择 ArrayList 的场景
    需要频繁随机访问元素,且插入/删除操作多在列表末尾。
    典型应用:数据缓存、配置项列表。

  • 选择 LinkedList 的场景
    需要频繁在列表头部或中间插入/删除元素,且对随机访问需求较少。
    典型应用:实现队列(Queue)或双端队列(Deque)。

现代开发建议:大多数情况下优先使用 ArrayList,仅在需要高效头/尾操作且数据量较大时考虑 LinkedList

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值