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 代码。
更多推荐
所有评论(0)