Java List 详解:从基础到进阶的全面指南
引言
在 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
线程安全处理
ArrayList
和LinkedList
都是线程不安全的,在多线程环境下可使用:
// 方式1:使用同步包装器
List<String> syncList = Collections.synchronizedList(new ArrayList<>());// 方式2:使用CopyOnWriteArrayList(适合读多写少场景)
List<String> cowList = new CopyOnWriteArrayList<>();
注意事项
初始容量设置:
ArrayList
默认初始容量为 10,当元素满时会自动扩容(通常为 1.5 倍),频繁扩容影响性能。已知大致规模时,建议指定初始容量。避免使用
Vector
:Vector
是线程安全的,但性能较差,推荐使用Collections.synchronizedList()
或CopyOnWriteArrayList
替代。null
元素处理:List 允许存储null
,但操作时需注意NullPointerException
,建议避免存储null
或在操作前判断。equals 与 hashCode:当存储自定义对象时,务必重写
equals()
和hashCode()
方法,否则contains()
、indexOf()
等方法可能无法正常工作。
总结
List
作为 Java 中最常用的集合类型,掌握其特性和用法对开发者至关重要。在实际开发中,应根据具体场景选择合适的实现类:
- 频繁随机访问:选择
ArrayList
- 频繁插入删除:选择
LinkedList
- 多线程环境:选择
CopyOnWriteArrayList
或同步包装器
熟练运用List
的各种操作,并注意其线程安全性和性能特性,能帮助我们编写更高效、更健壮的 Java 代码。