引言

在 Java 编程中,集合框架是日常开发不可或缺的部分,而List作为其中最常用的接口之一,掌握其特性和用法对编写高效代码至关重要。本文将从基础概念到高级用法,全面解析 Java 中的List接口。

什么是 List?

List是 Java 集合框架中的一个接口(位于java.util包下),它继承自Collection接口,代表一个有序、可重复的元素集合。与Set不同,List允许存储重复元素,并且保持元素的插入顺序,支持通过索引访问元素,这使它成为处理序列数据的理想选择。

常用的 List 实现类

List作为接口不能直接实例化,需要使用其实现类。选择合适的实现类对性能有重要影响:

实现类底层结构核心特点适用场景
ArrayList动态数组随机访问效率高(O (1)),中间插入 / 删除效率低(O (n)),内存占用较连续读多写少、需要频繁随机访问的场景
LinkedList双向链表随机访问效率低(O (n)),插入 / 删除效率高(O (1)),内存占用较分散写多读少、频繁在首尾操作的场景(如队列)
Vector动态数组ArrayList类似,但支持线程同步(效率较低)多线程环境(已基本被ArrayList替代)
CopyOnWriteArrayList动态数组(线程安全)写入时复制整个数组,读操作无锁,适合读多写极少的并发场景高并发读场景

在大多数情况下,ArrayList是首选,因为它在大多数常见操作中表现更优。

List 的基本操作

下面以ArrayList为例,介绍List的核心操作:

1. 初始化 List

// 基本初始化
List<String> fruits = new ArrayList<>();

// 指定初始容量(减少扩容开销)
List<Integer> numbers = new ArrayList<>(20);

// 从其他集合初始化
List<String> otherList = Arrays.asList("apple", "banana");
List<String> fruitList = new ArrayList<>(otherList);

// JDK9+ 便捷初始化(不可修改)
List<String> fixedList = List.of("a", "b", "c");

注意:List.of()创建的是不可修改列表,执行add/remove会抛出UnsupportedOperationException

2. 添加元素

List<String> list = new ArrayList<>();

// 添加元素到末尾
list.add("apple");
list.add("banana");

// 在指定位置插入元素
list.add(1, "orange"); // 结果: [apple, orange, banana]

// 批量添加
List<String> moreFruits = Arrays.asList("grape", "mango");
list.addAll(moreFruits); // 结果: [apple, orange, banana, grape, mango]

3. 获取元素

List<String> list = new ArrayList<>(Arrays.asList("apple", "orange", "banana"));

// 通过索引获取
String first = list.get(0); // "apple"

// 获取元素索引
int index = list.indexOf("banana"); // 2

// 最后一次出现的索引
list.add("apple");
int lastIndex = list.lastIndexOf("apple"); // 3

4. 修改元素

List<String> list = new ArrayList<>(Arrays.asList("apple", "orange", "banana"));

// 替换指定位置元素
list.set(1, "pear"); // 结果: [apple, pear, banana]

5. 删除元素

List<String> list = new ArrayList<>(Arrays.asList("apple", "pear", "banana"));

// 删除指定索引元素
list.remove(0); // 结果: [pear, banana]

// 删除指定元素
list.remove("pear"); // 结果: [banana]

// 批量删除
List<String> toRemove = Arrays.asList("banana");
list.removeAll(toRemove); // 结果: []

// 清空列表
list.clear();

6. 查找与判断

List<String> list = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));

// 判断是否包含元素
boolean hasApple = list.contains("apple"); // true

// 判断是否为空
boolean isEmpty = list.isEmpty(); // false

// 获取元素数量
int size = list.size(); // 3

遍历 List 的多种方式

遍历是 List 最常用的操作之一,根据场景选择合适的遍历方式:

List<String> fruits = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));

// 1. 普通for循环(适合需要索引的场景)
for (int i = 0; i < fruits.size(); i++) {
    System.out.println(fruits.get(i));
}

// 2. 增强for循环(简洁,无需关心索引)
for (String fruit : fruits) {
    System.out.println(fruit);
}

// 3. 迭代器(支持边遍历边删除)
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
    String fruit = iterator.next();
    if (fruit.equals("banana")) {
        iterator.remove(); // 安全删除
    }
}

// 4. Lambda表达式(JDK8+,简洁优雅)
fruits.forEach(fruit -> System.out.println(fruit));

// 5. 流式操作(JDK8+,适合复杂处理)
fruits.stream()
      .filter(f -> f.startsWith("a"))
      .forEach(System.out::println);

进阶操作

排序

List<Integer> numbers = new ArrayList<>(Arrays.asList(3, 1, 4, 2));

// 自然排序(升序)
Collections.sort(numbers); // [1, 2, 3, 4]

// 自定义排序(降序)
numbers.sort(Collections.reverseOrder()); // [4, 3, 2, 1]

// 对象排序(以字符串长度为例)
List<String> words = new ArrayList<>(Arrays.asList("apple", "banana", "orange"));
words.sort(Comparator.comparingInt(String::length)); // 按长度排序

子列表操作

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e"));

// 获取子列表([1,4) 左闭右开)
List<String> subList = list.subList(1, 4); // [b, c, d]

// 注意:subList是原列表的视图,修改会影响原列表
subList.set(0, "x"); // 原列表变为 [a, x, c, d, e]

警告:对原列表进行结构修改(如 add/remove)会导致子列表抛出ConcurrentModificationException

线程安全处理

ArrayListLinkedList都是线程不安全的,在多线程环境下可使用:

// 方式1:使用同步包装器
List<String> syncList = Collections.synchronizedList(new ArrayList<>());

// 方式2:使用CopyOnWriteArrayList(适合读多写少场景)
List<String> cowList = new CopyOnWriteArrayList<>();

注意事项

  1. 初始容量设置ArrayList默认初始容量为 10,当元素满时会自动扩容(通常为 1.5 倍),频繁扩容影响性能。已知大致规模时,建议指定初始容量。

  2. 避免使用VectorVector是线程安全的,但性能较差,推荐使用Collections.synchronizedList()CopyOnWriteArrayList替代。

  3. null元素处理:List 允许存储null,但操作时需注意NullPointerException,建议避免存储null或在操作前判断。

  4. equals 与 hashCode:当存储自定义对象时,务必重写equals()hashCode()方法,否则contains()indexOf()等方法可能无法正常工作。

总结

List作为 Java 中最常用的集合类型,掌握其特性和用法对开发者至关重要。在实际开发中,应根据具体场景选择合适的实现类:

  • 频繁随机访问:选择ArrayList
  • 频繁插入删除:选择LinkedList
  • 多线程环境:选择CopyOnWriteArrayList或同步包装器

熟练运用List的各种操作,并注意其线程安全性和性能特性,能帮助我们编写更高效、更健壮的 Java 代码。

更多推荐