Java 中 ArrayList 与 LinkedList 的详细比较

文章目录

ArrayList 详细介绍

内部实现

ArrayList 是基于动态数组的数据结构。它允许随机访问元素,并且在内存中是连续存储的。当 ArrayList 的容量不足时,它会创建一个更大的数组,并将现有元素复制到新数组中,这可能会导致性能开销。内部使用的是 Object[] 数组来存储元素。

性能特点
  • 随机访问:O(1),因为可以直接通过索引访问。
  • 插入和删除(末尾):平均情况下 O(1);最坏情况下 O(n),如果需要扩容的话。
  • 插入和删除(中间或开头):O(n),因为需要移动元素以保持数组的连续性。
使用场景
  • 当你需要频繁地进行随机访问或者在列表末尾添加/移除元素时,ArrayList 是一个很好的选择。
  • 如果你预计列表的大小不会频繁变化,那么 ArrayList 也可以是一个合适的选择,因为它提供了较快的读取速度。
java 复制代码
import java.util.ArrayList;

public class ArrayListExample {
    public static void main(String[] args) {
        // 创建一个ArrayList实例,默认初始容量为10
        ArrayList<String> list = new ArrayList<>();
        
        // 添加元素到ArrayList
        // add() 方法在列表末尾添加元素,时间复杂度为 O(1)
        list.add("Apple");
        list.add("Banana");
        list.add("Orange");
        
        // 打印列表,toString() 方法返回列表内容的字符串表示
        System.out.println("Initial List: " + list);
        
        // 随机访问元素,get() 方法根据索引获取元素,时间复杂度为 O(1)
        String element = list.get(1); // 获取索引为1的元素
        System.out.println("Element at index 1: " + element);
        
        // 在末尾添加元素,add() 方法在列表末尾添加元素,时间复杂度为 O(1)
        list.add("Grapes");
        System.out.println("After adding Grapes: " + list);
        
        // 删除元素,remove() 方法根据索引删除元素,时间复杂度为 O(n)
        list.remove(2); // 删除索引为2的元素
        System.out.println("After removing index 2: " + list);
        
        // 设置元素值,set() 方法替换指定位置的元素,时间复杂度为 O(1)
        list.set(1, "Mango"); // 将索引为1的元素设置为 "Mango"
        System.out.println("After setting index 1 to Mango: " + list);
        
        // 检查是否包含某个元素,contains() 方法遍历列表查找元素,时间复杂度为 O(n)
        boolean containsApple = list.contains("Apple");
        System.out.println("Contains Apple: " + containsApple);
        
        // 获取列表大小,size() 方法返回列表中元素的数量,时间复杂度为 O(1)
        int size = list.size();
        System.out.println("List size: " + size);
    }
}

LinkedList 详细介绍

内部实现

LinkedList 是基于双向链表的数据结构。每个节点不仅包含元素本身,还包含指向前后节点的引用。这种结构使得在链表两端进行插入和删除操作非常高效。内部使用了 Node<E> 类来表示链表中的节点。

性能特点
  • 随机访问:O(n),因为必须遍历链表到达指定位置。
  • 插入和删除(两端):O(1),不需要移动其他元素。
  • 插入和删除(中间):O(n),首先需要找到目标位置。
使用场景
  • 当你需要频繁地在列表的任意位置进行插入或删除操作时,LinkedList 提供了更好的性能。
  • 如果你需要一个队列或双端队列的数据结构,LinkedList 实现了 Deque 接口,可以高效地支持这些数据结构所需的头部和尾部操作。
java 复制代码
import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        // 创建一个LinkedList实例
        LinkedList<String> list = new LinkedList<>();
        
        // 添加元素到LinkedList
        // add() 方法在列表末尾添加元素,时间复杂度为 O(1)
        list.add("Apple");
        list.add("Banana");
        list.add("Orange");
        
        // 打印列表,toString() 方法返回列表内容的字符串表示
        System.out.println("Initial List: " + list);
        
        // 使用迭代器遍历元素,iterator() 方法返回一个用于遍历列表的迭代器
        for (String fruit : list) {
            System.out.println(fruit);
        }
        
        // 在头部插入元素,addFirst() 方法在列表头部添加元素,时间复杂度为 O(1)
        list.addFirst("Strawberry");
        System.out.println("After adding to head: " + list);
        
        // 在尾部添加元素,addLast() 方法在列表尾部添加元素,时间复杂度为 O(1)
        list.addLast("Grapes");
        System.out.println("After adding to tail: " + list);
        
        // 删除第一个和最后一个元素,removeFirst() 和 removeLast() 方法分别删除头尾元素,时间复杂度为 O(1)
        list.removeFirst();
        list.removeLast();
        System.out.println("After removing first and last: " + list);
        
        // 获取第一个和最后一个元素,getFirst() 和 getLast() 方法分别获取头尾元素,时间复杂度为 O(1)
        String first = list.getFirst();
        String last = list.getLast();
        System.out.println("First Element: " + first);
        System.out.println("Last Element: " + last);
        
        // 插入元素到指定位置,add(int index, E element) 方法在指定位置插入元素,时间复杂度为 O(n)
        list.add(1, "Peach");
        System.out.println("After inserting Peach at index 1: " + list);
        
        // 删除指定位置的元素,remove(int index) 方法删除指定位置的元素,时间复杂度为 O(n)
        list.remove(1);
        System.out.println("After removing element at index 1: " + list);
    }
}

线程安全性与同步

默认情况下,ArrayListLinkedList 都不是线程安全的。如果在多线程环境中使用它们,你需要确保适当的同步。可以使用 Collections.synchronizedList() 方法来包装列表,以获得线程安全的版本。但是需要注意,即使列表被同步了,迭代器仍然不是线程安全的。

java 复制代码
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

public class SynchronizedListExample {
    public static void main(String[] args) {
        // 创建一个ArrayList实例,并用 Collections.synchronizedList 包装以获得线程安全的版本
        List<String> syncList = Collections.synchronizedList(new ArrayList<>());
        // 或者对于 LinkedList
        // List<String> syncList = Collections.synchronizedList(new LinkedList<>());

        // 对于同步列表的操作...
        synchronized (syncList) {
            syncList.add("Thread-safe Element");
        }
        
        // 注意:即使列表是线程安全的,迭代器也不是线程安全的。
        // 必须在外层加锁来保证迭代期间的安全性。
        synchronized (syncList) {
            for (String item : syncList) {
                // 迭代操作...
            }
        }
    }
}

迭代器失效

当在迭代过程中修改了 ArrayListLinkedList,除了通过迭代器自身的 removeadd 方法外,都会抛出 ConcurrentModificationException。这是为了防止并发修改带来的不一致性问题。

java 复制代码
import java.util.Iterator;
import java.util.ArrayList;

public class IteratorExample {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");

        // 获取迭代器
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String fruit = it.next();
            if ("Banana".equals(fruit)) {
                // 正确的方式:通过迭代器移除元素,避免 ConcurrentModificationException
                it.remove();
            }
            // 错误的方式:直接通过list.remove()会导致ConcurrentModificationException
            // list.remove(fruit);
        }

        // 打印最终列表
        System.out.println("Final List after iteration removal: " + list);
    }
}
相关推荐
wenxin-4 分钟前
NS3网络模拟器中如何利用Gnuplot工具像MATLAB一样绘制各类图形?
开发语言·matlab·画图·ns3·lr-wpan
数据小爬虫@2 小时前
深入解析:使用 Python 爬虫获取苏宁商品详情
开发语言·爬虫·python
健胃消食片片片片2 小时前
Python爬虫技术:高效数据收集与深度挖掘
开发语言·爬虫·python
王老师青少年编程3 小时前
gesp(C++五级)(14)洛谷:B4071:[GESP202412 五级] 武器强化
开发语言·c++·算法·gesp·csp·信奥赛
gxhlh3 小时前
局域网中 Windows 与 Mac 互相远程连接的最佳方案
windows·macos
空の鱼4 小时前
java开发,IDEA转战VSCODE配置(mac)
java·vscode
一只小bit4 小时前
C++之初识模版
开发语言·c++
P7进阶路5 小时前
Tomcat异常日志中文乱码怎么解决
java·tomcat·firefox
王磊鑫5 小时前
C语言小项目——通讯录
c语言·开发语言
钢铁男儿5 小时前
C# 委托和事件(事件)
开发语言·c#