# Java List完全指南:从入门到高阶应用
一、List接口:有序集合的核心
作为Java集合框架中最常用的接口之一,List代表了有序可重复的元素序列。与Set的无序唯一特性形成鲜明对比,List更像是我们日常生活中的排队场景——先来后到很重要,且允许相同元素存在。
1.1 List核心特性
- 有序性:元素按插入顺序存储(非排序顺序)
- 可重复:允许存储相同元素的多个实例
- 索引访问:支持基于下标的高效随机访问
- 丰富API:提供位置相关的操作方法
1.2 List主要实现类对比
实现类 | 数据结构 | 线程安全 | 随机访问效率 | 增删效率 | 适用场景 |
---|---|---|---|---|---|
ArrayList | 动态数组 | 不安全 | O(1) | O(n) | 查询多、增删少的场景 |
LinkedList | 双向链表 | 不安全 | O(n) | O(1) | 频繁插入删除的场景 |
Vector | 动态数组 | 安全 | O(1) | O(n) | 需要线程安全的场景(已逐渐被淘汰) |
CopyOnWriteArrayList | 动态数组 | 安全 | O(1) | O(n) | 读多写少的并发场景 |
二、基础操作实战
2.1 创建与初始化
// 最常用的初始化方式(Java 7+)
List<String> arrayList = new ArrayList<>(); // 使用Arrays工具类快速初始化
List<Integer> numbers = Arrays.asList(1, 2, 3); // Java 9+ 的工厂方法
List<String> immutableList = List.of("A", "B", "C");// 指定初始容量(优化技巧)
List<Object> optimizedList = new ArrayList<>(100);
2.2 增删改查四连击
List<String> fruits = new ArrayList<>();// 增(尾部添加)
fruits.add("Apple");
fruits.add(0, "Banana"); // 指定位置插入// 删
fruits.remove("Apple"); // 按元素删除
fruits.remove(0); // 按索引删除// 改
fruits.set(0, "Mango"); // 替换指定位置元素// 查
String first = fruits.get(0); // 获取元素
int index = fruits.indexOf("Mango"); // 查找位置
boolean exists = fruits.contains("Apple"); // 存在判断
2.3 遍历方式大比拼
// 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> it = fruits.iterator();
while (it.hasNext()) {String fruit = it.next();if (fruit.equals("Mango")) {it.remove(); // 安全删除}
}// 4. Java 8+ forEach + Lambda
fruits.forEach(fruit -> System.out.println(fruit));
三、进阶技巧与最佳实践
3.1 容量优化之道
ArrayList内部使用Object数组存储数据,默认初始容量为10。当元素数量达到当前容量时,会自动进行1.5倍扩容(int newCapacity = oldCapacity + (oldCapacity >> 1)
),这个扩容操作会导致数组复制,影响性能。
优化建议:
// 预估数据量较大时指定初始容量
List<BigData> bigList = new ArrayList<>(10000);// 已知最终大小时使用一次性扩容
ArrayList<String> optimized = new ArrayList<>();
optimized.ensureCapacity(5000); // 预先扩容
3.2 不可变列表的多种实现
// 1. Java 9+的不可变列表
List<String> immutable1 = List.of("A", "B", "C");// 2. Collections工具类
List<String> immutable2 = Collections.unmodifiableList(mutableList);// 3. Guava库的不可变列表
ImmutableList<String> immutable3 = ImmutableList.of("A", "B", "C");// 尝试修改将抛出UnsupportedOperationException
immutable1.add("D"); // 抛出异常
3.3 高效列表合并技巧
List<String> list1 = Arrays.asList("A", "B");
List<String> list2 = Arrays.asList("C", "D");// 方法1:使用addAll(会修改原集合)
List<String> merged = new ArrayList<>(list1);
merged.addAll(list2);// 方法2:Java 8 Stream API(不修改原集合)
List<String> merged2 = Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList());// 方法3:Guava库的Iterables.concat
Iterable<String> merged3 = Iterables.concat(list1, list2);
四、性能陷阱与规避方案
4.1 随机访问的坑
LinkedList<String> linkedList = new LinkedList<>();
// 填充10000个元素...// 反模式:链表随机访问(效率O(n))
for (int i = 0; i < linkedList.size(); i++) {String item = linkedList.get(i); // 性能灾难!
}// 正确做法:使用迭代器
for (Iterator<String> it = linkedList.iterator(); it.hasNext(); ) {String item = it.next(); // 效率O(1)
}
4.2 并发修改异常
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));// 错误示例:遍历时修改
for (String s : list) {if (s.equals("B")) {list.remove(s); // 抛出ConcurrentModificationException}
}// 解决方案1:使用迭代器的remove方法
Iterator<String> it = list.iterator();
while (it.hasNext()) {if (it.next().equals("B")) {it.remove(); // 安全删除}
}// 解决方案2:Java 8+ removeIf
list.removeIf(s -> s.equals("B"));
五、实际应用案例
5.1 分页查询实现
public static <T> List<T> getPage(List<T> sourceList, int page, int pageSize) {if (sourceList == null || sourceList.isEmpty()) {return Collections.emptyList();}int totalItems = sourceList.size();int fromIndex = (page - 1) * pageSize;if (fromIndex >= totalItems) {return Collections.emptyList();}int toIndex = Math.min(fromIndex + pageSize, totalItems);return sourceList.subList(fromIndex, toIndex);
}// 使用示例
List<Integer> numbers = IntStream.range(0, 100).boxed().collect(Collectors.toList());List<Integer> page3 = getPage(numbers, 3, 10); // 获取第3页数据
5.2 列表排序大全
List<String> names = Arrays.asList("John", "Alice", "Bob");// 1. 自然排序
Collections.sort(names); // 原地修改
List<String> sorted = names.stream().sorted().toList(); // Java 16+// 2. 自定义比较器
names.sort(Comparator.comparing(String::length)); // 按长度排序// 3. 多条件排序
List<Person> people = getPeople();
people.sort(Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName));// 4. 逆序排列
names.sort(Comparator.reverseOrder());
六、与数组的转换技巧
// List → 数组
List<String> list = Arrays.asList("A", "B", "C");
String[] array1 = list.toArray(new String[0]); // 推荐方式
String[] array2 = list.toArray(String[]::new); // Java 11+// 数组 → List
String[] arr = {"X", "Y", "Z"};
List<String> fromArray1 = Arrays.asList(arr); // 固定大小列表
List<String> fromArray2 = new ArrayList<>(Arrays.asList(arr)); // 可变列表
List<String> fromArray3 = Stream.of(arr).collect(Collectors.toList()); // Java 8
总结与选择建议
- 默认选择:大多数情况下优先使用
ArrayList
,除非有频繁的插入删除操作 - 线程安全:考虑
Collections.synchronizedList
或CopyOnWriteArrayList
- 性能敏感:预估大小并设置初始容量,避免频繁扩容
- 不可变需求:使用
List.of()
或不可变集合库 - 复杂操作:善用Stream API进行过滤/映射/归约等操作
记住:没有最好的实现,只有最适合场景的实现。理解每种List实现的内在机制,才能在实际开发中做出最优选择。