Java List集合

在Java编程中,集合框架是我们最常用的工具之一。而List作为集合框架的核心接口,以其有序、可重复的特性,成为日常开发中的"常客"。无论是存储查询结果、管理用户列表,还是实现队列/栈等数据结构,List都扮演着重要角色。

本文将全面介绍List集合的特点、常用实现类、核心API以及实际应用场景,帮助你深入理解并灵活运用List集合。

一、List集合概述

1.1 什么是List?

List接口继承自Collection接口,是Java集合框架中的重要组成部分。它代表了一个有序的、可重复的元素集合,用户可以精确控制每个元素的插入位置,并通过整数索引访问元素。

1.2 List的核心特点

与Set、Map等其他集合类型相比,List具有三个鲜明特征:

特点 说明 示例
有序性 元素存入的顺序与取出的顺序一致 存入"A","B","C",取出时仍然是"A","B","C"
可重复 允许存储相同的元素 可以添加两个相同的"hello"
带索引 可以通过索引精确操作元素 list.get(0)list.remove(2)

1.3 集合与数组的区别

理解List之前,先明确集合与数组的本质差异:

对比维度 数组 集合
长度 固定不变 动态可变
存储类型 可存基本类型和引用类型 只能存引用类型
访问方式 通过索引 迭代器、增强for等多种方式

二、List的主要实现类

List接口有多个实现类,其中最常用的是ArrayListLinkedListVector。它们各有特点,适用于不同场景。

2.1 ArrayList:动态数组实现

底层数据结构:Object[]数组

特点

  • 查询效率高(通过索引直接定位,时间复杂度O(1))

  • 增删效率一般(特别是中间位置插入/删除,需要移动元素)

  • 线程不安全

适用场景:频繁的随机访问操作、尾部插入为主

扩容机制

  • 默认初始容量为10

  • 当容量不足时,新容量 = 旧容量 × 1.5

  • JDK 7:构造时直接创建长度为10的数组(饿汉式)

  • JDK 8:构造时创建空数组,第一次add时才创建长度为10的数组(懒汉式,节省内存)

2.2 LinkedList:双向链表实现

底层数据结构:双向链表

java 复制代码
private static class Node<E> {
    E item;           // 当前元素
    Node<E> next;     // 后一个节点引用
    Node<E> prev;     // 前一个节点引用
}

特点

  • 查询效率一般(需要遍历查找,时间复杂度O(n))

  • 增删效率高(只需修改节点引用)

  • 线程不安全

  • 提供了丰富的首尾操作方方法(addFirstaddLastremoveFirst等)

适用场景:频繁的插入/删除操作、实现队列或栈

2.3 Vector:线程安全的动态数组

特点

  • 与ArrayList类似,基于数组实现

  • 线程安全(方法用synchronized修饰)

  • 扩容机制不同:默认扩容为原数组的2倍

  • 性能比ArrayList低

适用场景 :需要线程安全的场景(但现代Java并发首选CopyOnWriteArrayList

2.4 实现类对比一览

特性 ArrayList LinkedList Vector
数据结构 动态数组 双向链表 动态数组
随机访问 O(1) O(n) O(1)
插入/删除 O(n)(尾部O(1)) O(1)(头尾) O(n)
线程安全
扩容倍数 1.5倍 不需扩容 2倍

三、List常用API详解

List除了继承Collection的方法外,还增加了大量索引相关的方法。

3.1 核心方法

java 复制代码
// 创建List集合(多态写法)
List<String> list = new ArrayList<>();

// 1. 添加元素
list.add("Java");              // 尾部添加
list.add(1, "Python");         // 指定位置添加

// 2. 获取元素
String lang = list.get(0);      // 获取索引0的元素

// 3. 修改元素
list.set(1, "C++");             // 替换索引1的元素

// 4. 删除元素
list.remove(0);                 // 删除索引0的元素
list.remove("Java");            // 删除指定元素(首次出现)

// 5. 其他操作
int size = list.size();         // 集合长度
boolean exists = list.contains("Java");  // 是否包含
int index = list.indexOf("C++");         // 查找元素位置
boolean isEmpty = list.isEmpty();        // 是否为空

3.2 LinkedList特有方法

LinkedList提供了丰富的首尾操作方法,使其非常适合实现队列或栈:

java 复制代码
LinkedList<String> linkedList = new LinkedList<>();

// 首部操作
linkedList.addFirst("First");    // 头部添加
linkedList.push("Push");         // 等效于addFirst
String first = linkedList.getFirst();   // 获取头部
String removedFirst = linkedList.removeFirst(); // 移除头部
String pop = linkedList.pop();           // 等效于removeFirst

// 尾部操作
linkedList.addLast("Last");      // 尾部添加
String last = linkedList.getLast();      // 获取尾部
String removedLast = linkedList.removeLast(); // 移除尾部

// 判断空
boolean empty = linkedList.isEmpty();

四、List的遍历方式

List支持多种遍历方式,各有适用场景:

4.1 六种遍历方法

java 复制代码
List<String> list = Arrays.asList("A", "B", "C");

// 1. 普通for循环(带索引)
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

// 2. 增强for循环(最简洁)
for (String item : list) {
    System.out.println(item);
}

// 3. 迭代器(Iterator)
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
    // iterator.remove(); // 可在遍历时安全删除
}

// 4. 列表迭代器(ListIterator)- 支持双向遍历和修改
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
    String item = listIterator.next();
    if ("B".equals(item)) {
        listIterator.remove();    // 删除
        listIterator.add("D");    // 插入
        listIterator.set("E");    // 替换
    }
}

// 5. forEach(Lambda表达式)
list.forEach(item -> System.out.println(item));

// 6. Stream流
list.stream().forEach(System.out::println);
list.parallelStream().forEach(System.out::println); // 并行流

4.2 并发修改异常

重要提醒 :使用迭代器遍历时,不能 通过集合对象修改元素(如list.add()),否则会抛出ConcurrentModificationException

java 复制代码
// 错误示例:会抛出异常
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if ("B".equals(it.next())) {
        list.add("D");  //  并发修改异常!
    }
}

// 正确做法:使用迭代器的remove方法,或使用普通for循环
for (int i = 0; i < list.size(); i++) {
    if ("B".equals(list.get(i))) {
        list.add("D");  // 普通for循环允许修改
    }
}

五、线程安全的List

5.1 实现线程安全的三种方式

java 复制代码
// 方式1:使用Collections工具类包装
List<String> safeList = Collections.synchronizedList(new ArrayList<>());

// 方式2:使用Vector(古老实现,不推荐)
List<String> vector = new Vector<>();

// 方式3:使用CopyOnWriteArrayList(推荐)
List<String> copyOnWriteList = new CopyOnWriteArrayList<>();

5.2 CopyOnWriteArrayList原理

CopyOnWriteArrayList是并发包下的线程安全List,采用读写分离策略:

  • 写操作:加锁(ReentrantLock),复制新数组,修改后替换原数组引用

  • 读操作:无锁,直接读取(可能读到旧数据,但保证最终一致性)

  • 优点:读操作极高并发,适合读多写少场景

  • 缺点:写操作开销大(复制数组),数据弱一致性


六、实际应用场景与选择建议

6.1 场景对比

场景 推荐实现 原因
频繁随机访问(如排行榜) ArrayList 索引访问O(1)
尾部插入为主(如日志收集) ArrayList 尾部插入O(1)
频繁中间插入/删除 LinkedList 只需修改节点引用
队列(FIFO) LinkedList 提供了removeFirst()等方法
栈(LIFO) LinkedList 提供了push()/pop()方法
多线程环境读多写少 CopyOnWriteArrayList 读写分离,并发度高
多线程环境读写频繁 Collections.synchronizedList() 平衡性能与安全

6.2 简单案例:用户管理系统

java 复制代码
public class UserManager {
    private List<String> users;
    
    public UserManager() {
        // 根据场景选择实现类
        users = new ArrayList<>();  // 查询为主
        // users = new LinkedList<>(); // 插入删除为主
    }
    
    public void addUser(String user) {
        users.add(user);
    }
    
    public void removeUser(String user) {
        users.remove(user);
    }
    
    public void printUsers() {
        users.forEach(System.out::println);
    }
    
    public static void main(String[] args) {
        UserManager manager = new UserManager();
        manager.addUser("Alice");
        manager.addUser("Bob");
        manager.addUser("Charlie");
        
        manager.printUsers();
        manager.removeUser("Bob");
        System.out.println("--- 删除后 ---");
        manager.printUsers();
    }
}

七、总结

List集合是Java开发中最基础、最常用的数据结构之一。掌握List,需要理解:

  1. 三大特点:有序、可重复、带索引

  2. 两大主力实现:ArrayList(数组,查询快)和LinkedList(链表,增删快)

  3. 一大线程安全选择:CopyOnWriteArrayList(读多写少场景)

  4. 多种遍历方式:根据需求选择合适的遍历方法

相关推荐
OxyTheCrack1 小时前
【C++】详细拆解std::mutex的底层原理
linux·开发语言·c++·笔记
左左右右左右摇晃2 小时前
红黑树笔记整理
笔记
不想看见4044 小时前
Reverse Bits位运算基础问题--力扣101算法题解笔记
笔记·算法·leetcode
observe1014 小时前
在线商城项目笔记 3.11
笔记
QD_ANJING4 小时前
3月面大厂前端岗总结笔记(含答案)
前端·javascript·笔记·面试·职场和发展·前端框架·pdf
九成宫5 小时前
计算机网络期末复习——第5章:链路层 Part Two
网络·笔记·计算机网络·软件工程
愚昧之山绝望之谷开悟之坡6 小时前
什么是nacos
笔记
hy15687866 小时前
MDL (Multi-Scenario Denoising Learning) 笔记
笔记
困死,根本不会7 小时前
蓝桥杯python备赛笔记之(八)动态规划(DP)
笔记·python·学习·算法·蓝桥杯·动态规划