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);
    }
}
相关推荐
㎝㎜24 分钟前
Windows系统搭建Docker
windows·docker·容器
漂流瓶66666624 分钟前
Scala的模式匹配变量类型
开发语言·后端·scala
夏天吃哈密瓜30 分钟前
Scala中的正则表达式01
大数据·开发语言·后端·正则表达式·scala
2401_8337880532 分钟前
Scala的模式匹配(2)
java·开发语言
Lbs_gemini060332 分钟前
C++研发笔记14——C语言程序设计初阶学习笔记12
c语言·开发语言·c++·笔记·学习
ac-er88881 小时前
GD库如何根据颜色生成纯色背景图
开发语言·php
悠悠龙龙2 小时前
框架模块说明 #05 权限管理_03
java·开发语言·spring
开心羊咩咩3 小时前
Idea 2024.3 突然出现点击run 运行没有反应,且没有任何提示。
java·ide·intellij-idea
waterme1onY3 小时前
IDEA中MAVEN的一些设置问题
java·maven·intellij-idea
阿华的代码王国3 小时前
【算法】——前缀和(矩阵区域和详解,文末附)
java·开发语言·算法·前缀和