【面试题】ArrayList 和 LinkedList的区别

理论认知

ArrayListLinkedList 都是 Java 集合框架(Java Collections Framework)中的一部分,它们用于存储动态大小的元素列表。然而,它们在内部实现、性能特性以及用途上有一些显著的差异。以下是它们之间的主要区别:

  1. 内部实现

    • ArrayList:基于动态数组实现。在内存中,它使用连续的空间来存储元素。当元素被添加到列表中时,如果数组的大小不足以容纳新元素,则创建一个更大的数组,并将旧数组的内容复制到新数组中。
    • LinkedList:基于双向链表实现。每个元素(称为节点)都包含数据和指向其前一个节点和后一个节点的引用。因此,它不需要在内存中占用连续的空间。
  2. 性能特性

    • 访问元素:由于 ArrayList 使用连续的内存空间,因此可以通过索引直接访问任何位置的元素,时间复杂度为 O(1)。而 LinkedList 需要从头或尾节点开始遍历链表以访问特定位置的元素,时间复杂度为 O(n)。
    • 添加/删除元素:在 ArrayList 的末尾添加或删除元素是高效的(O(1)),但在列表的开头或中间添加或删除元素则可能需要移动大量元素(O(n))。相反,LinkedList 在列表的开头或结尾添加或删除元素是高效的(O(1)),因为只需修改相邻节点的引用即可。在列表的中间添加或删除元素需要遍历链表以找到正确的位置(O(n)),但不需要移动大量元素。
  3. 空间占用

    • ArrayList 在内存中占用连续的空间,因此空间利用率较高。但是,当数组大小不足以容纳新元素时,它可能会创建更大的数组,这可能会导致空间浪费。
    • LinkedList 由于使用链表结构,不需要连续的内存空间,因此更灵活。但是,由于每个节点都需要额外的空间来存储引用,因此其空间占用可能会稍大一些。
  4. 线程安全

    • ArrayListLinkedList 都不是线程安全的。如果在多线程环境中使用它们,并且多个线程同时修改列表,可能会导致数据不一致或其他并发问题。如果需要线程安全的列表,可以使用 Collections.synchronizedList() 方法或 CopyOnWriteArrayListConcurrentLinkedQueue 等并发集合类。
  5. 用途

    • ArrayList 通常用于需要频繁访问元素但不经常添加或删除元素的情况。由于其高效的索引访问和较低的空间占用,它是许多应用程序的首选数据结构。
    • LinkedList 通常用于需要频繁在列表的开头或结尾添加或删除元素的情况。由于其双向链表结构,它在这些操作上具有优势。此外,它还可以用作栈(使用 push()pop() 方法)或队列(使用 offer()poll()peek() 方法)。

下面我将使用Java代码来展示ArrayListLinkedList的一些基本操作,并突出它们之间的区别。

ArrayList 示例

import java.util.ArrayList;

public class ArrayListExample {
    public static void main(String[] args) {
        // 创建一个 ArrayList
        ArrayList<String> arrayList = new ArrayList<>();

        // 添加元素到 ArrayList 的末尾
        arrayList.add("Element 1");
        arrayList.add("Element 2");
        arrayList.add("Element 3");

        // 访问 ArrayList 中的元素(通过索引)
        System.out.println("Element at index 1: " + arrayList.get(1));

        // 在 ArrayList 的开头添加元素
        // 注意:这可能需要移动所有现有的元素,因此效率较低
        arrayList.add(0, "Element 0");

        // 删除 ArrayList 中的元素(通过索引)
        // 注意:这可能需要移动被删除元素之后的所有元素,因此效率较低
        arrayList.remove(2);

        // 遍历 ArrayList
        for (String element : arrayList) {
            System.out.println(element);
        }
    }
}

LinkedList 示例

下滑查看解决方法

import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        // 创建一个 LinkedList
        LinkedList<String> linkedList = new LinkedList<>();

        // 添加元素到 LinkedList 的末尾
        linkedList.add("Element 1");
        linkedList.add("Element 2");
        linkedList.add("Element 3");

        // 访问 LinkedList 中的元素(通常不这样做,因为效率较低)
        // 但可以通过迭代器或从头/尾节点访问
        // 这里为了演示仍然使用 get(index)
        System.out.println("Element at index 1: " + linkedList.get(1));

        // 在 LinkedList 的开头添加元素
        // 这是 LinkedList 的优势之一,因为它不需要移动任何元素
        linkedList.addFirst("Element 0");

        // 在 LinkedList 的结尾删除元素
        // 这是另一个优势,因为它也不需要移动任何元素
        linkedList.removeLast();

        // 遍历 LinkedList(通常使用迭代器,但为了简单这里使用 for-each)
        for (String element : linkedList) {
            System.out.println(element);
        }

        // 使用 LinkedList 作为栈(使用 push 和 pop)
        linkedList.push("New Element as Stack");
        System.out.println(linkedList.pop()); // 删除并返回栈顶元素

        // 使用 LinkedList 作为队列(使用 offer, poll, peek)
        linkedList.offer("New Element as Queue");
        System.out.println(linkedList.peek()); // 查看队列头元素但不删除
        System.out.println(linkedList.poll()); // 删除并返回队列头元素
    }
}

在上面的示例中,你可以看到ArrayListLinkedList在添加、删除和访问元素时的不同。特别是,当在列表的开头或中间添加/删除元素时,LinkedList的效率更高,因为它不需要移动大量元素。而ArrayList在访问元素时效率更高,因为它可以通过索引直接访问。同时,LinkedList还提供了作为栈和队列使用的额外方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值