当前位置: 首页 > article >正文

Java 中 ArrayList 与 LinkedList 的详细比较

文章目录

      • ArrayList 详细介绍
        • 内部实现
        • 性能特点
        • 使用场景
      • LinkedList 详细介绍
        • 内部实现
        • 性能特点
        • 使用场景
      • 线程安全性与同步
      • 迭代器失效

ArrayList 详细介绍

内部实现

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

性能特点
  • 随机访问:O(1),因为可以直接通过索引访问。
  • 插入和删除(末尾):平均情况下 O(1);最坏情况下 O(n),如果需要扩容的话。
  • 插入和删除(中间或开头):O(n),因为需要移动元素以保持数组的连续性。
使用场景
  • 当你需要频繁地进行随机访问或者在列表末尾添加/移除元素时,ArrayList 是一个很好的选择。
  • 如果你预计列表的大小不会频繁变化,那么 ArrayList 也可以是一个合适的选择,因为它提供了较快的读取速度。
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 接口,可以高效地支持这些数据结构所需的头部和尾部操作。
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() 方法来包装列表,以获得线程安全的版本。但是需要注意,即使列表被同步了,迭代器仍然不是线程安全的。

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。这是为了防止并发修改带来的不一致性问题。

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);
    }
}

http://www.kler.cn/a/420098.html

相关文章:

  • GitLab使用中遇到的一些问题-记录
  • Android矩阵Matrix在1张宽平大Bitmap批量绘制N个小Bitmap,Kotlin(1)
  • Linux-虚拟环境
  • 【设计模式系列】单例模式(二十)
  • LLM*:路径规划的大型语言模型增强增量启发式搜索
  • HarmonyOS4+NEXT星河版入门与项目实战(23)------实现手机游戏摇杆功能
  • 什么是 KDE?
  • numpy.float8不存在;Python中,实现16位浮点数
  • 种花问题算法
  • 运维工作常用Shell脚本(Commonly Used Shell Scripts for Operation and Maintenance Work)
  • 深入解析 Python 异步编程中的 `gather`、`as_completed` 和 `wait`
  • SQL注入--基本概念
  • 01-标准库开发-STM32定时器
  • 为什么在服务器上设置 fish 为默认 shell, vscode remote ssh 默认还是 bash?
  • flink学习(13)—— 重试机制和维表join
  • 在 uniapp 项目中使用 Iconify 字体图标库
  • 《Python PDF 格式转换全攻略》
  • Linux 进程管理详解
  • 张量并行和流水线并行在Transformer中的具体部位
  • 25.4K Star 高效内存数据存储!特别好用的Redis 和 Memcached 替代品:Dragonfly!
  • redisson-spring-data与Spring-Data-Redis的版本关系问题
  • 性能监控系统Prometheus、Node-exporter与Grafana部署详解搭建
  • 黑马程序员Java项目实战《苍穹外卖》Day03
  • Xilinx PCIe高速接口入门实战(一)
  • 软件保护:从用户角度出发的安全需求与体验
  • C++之 String 类的模拟实现