上一篇
java list怎么使用方法
- 后端开发
- 2025-08-11
- 5
Java List通过add()添加元素,get(index)获取,size()查长度,可用
Java中的List
是java.util
包下的核心接口之一,用于表示有序、可重复的元素集合,它继承了Collection
接口的所有特性,并额外提供了基于索引的精确控制能力,以下是对其使用方法的系统性解析:
核心特征与常见实现类
特性 | 描述 |
---|---|
有序性 | 元素按插入顺序存储,可通过索引快速访问 |
允许重复 | 同一元素可多次出现 |
允许null值 | 可以包含null 元素 |
动态扩容 | 多数实现类支持自动扩容(如ArrayList ) |
多种底层结构 | 不同实现类采用数组/链表/栈等不同数据结构 |
主流实现类对比表
实现类 | 底层结构 | 增删效率 | 随机访问效率 | 适用场景 | 线程安全 |
---|---|---|---|---|---|
ArrayList |
动态数组 | 尾部快 头部慢 |
O(1) | 频繁随机访问/少量增删 | |
LinkedList |
双向链表 | 任意位置快 | O(n) | 频繁头尾操作/大数据量插入删除 | |
Vector |
动态数组 | 同ArrayList | O(1) | 古老线程安全版本(现推荐改用CopyOnWriteArrayList ) |
|
CopyOnWriteArrayList |
写时复制数组 | 写入开销大 | O(1) | 高并发读多写少场景 |
核心方法详解(以ArrayList
为例)
基础操作
import java.util.ArrayList; import java.util.List; public class ListDemo { public static void main(String[] args) { // 创建List实例 List<String> fruits = new ArrayList<>(); // 钻石操作符简化泛型声明 // 添加元素 fruits.add("Apple"); // 尾部追加 → [Apple] fruits.add("Banana"); // → [Apple, Banana] fruits.add(1, "Orange"); // 指定索引插入 → [Apple, Orange, Banana] // 获取元素 System.out.println("第二个元素: " + fruits.get(1)); // 输出 Orange // 修改元素 fruits.set(0, "Pear"); // 替换索引0元素 → [Pear, Orange, Banana] // 删除元素 fruits.remove(1); // 删除索引1 → [Pear, Banana] fruits.remove("Banana"); // 删除对象 → [Pear] // 判断存在性 boolean hasApple = fruits.contains("Apple"); // false } }
批量操作
方法 | 功能 | 示例 |
---|---|---|
addAll(Collection<? extends E> c) |
追加整个集合 | fruits.addAll(Arrays.asList("Grape", "Melon")) |
subList(int fromIndex, int toIndex) |
返回指定区间的子视图(原地修改影响源) | List<String> sub = fruits.subList(0, 2) |
clear() |
清空列表 | fruits.clear() |
特殊操作技巧
- 交换元素:需借助临时变量
String temp = fruits.get(i); fruits.set(i, fruits.get(j)); fruits.set(j, temp);
- 倒序遍历:结合
Collections.reverse()
或手动逆序索引 - 固定大小限制:创建时指定初始容量避免频繁扩容
new ArrayList<>(initialCapacity); // 推荐设置为预期大小的120%
关键实现类的特性差异
ArrayList
vs LinkedList
性能测试
操作类型 | ArrayList 耗时 |
LinkedList 耗时 |
原因分析 |
---|---|---|---|
尾部添加 | (最快) | 数组尾部直接赋值 | |
头部插入 | (最慢) | 数组需移动后续所有元素 | |
中间插入 | 链表只需修改前后指针 | ||
根据索引随机访问 | (最快) | (最慢) | 数组通过CPU缓存预取优化 |
遍历全部元素 | (最快) | 链表跳跃式访问无内存局部性 |
Vector
的特殊性
- 历史遗留的线程安全类,所有公共方法均使用
synchronized
修饰 - 已过时!现代开发推荐:
- 单线程环境:
ArrayList
- 多线程环境:
CopyOnWriteArrayList
(读多写少)或手动同步Collections.synchronizedList(new ArrayList<>())
- 单线程环境:
高级用法示例
排序与二分查找
List<Integer> numbers = new ArrayList<>(Arrays.asList(5, 3, 8, 1)); Collections.sort(numbers); // [1, 3, 5, 8] int index = Collections.binarySearch(numbers, 5); // 返回2(找到的位置)
自定义比较器排序
List<Student> students = ...; // Student类需实现Comparable或提供Comparator students.sort((a, b) -> a.getAge() b.getAge()); // 按年龄升序
不可变列表创建
List<String> immutableList = List.of("A", "B", "C"); // Java 9+ // 尝试修改会抛出UnsupportedOperationException immutableList.add("D"); // Error!
注意事项与最佳实践
-
避免空指针异常:
- 始终检查
size()
后再通过索引访问元素 - 使用
isEmpty()
判断空列表而非size()==0
- 始终检查
-
迭代器安全:
- 结构化修改(遍历时删除/添加)会导致
ConcurrentModificationException
- 解决方案:使用
Iterator
的remove()
方法或改用for
循环倒序删除
- 结构化修改(遍历时删除/添加)会导致
-
内存优化:
ArrayList
初始容量设为预计大小的120%可减少扩容次数- 高频插入/删除场景优先考虑
LinkedList
-
线程安全方案选择:
- 读多写少:
CopyOnWriteArrayList
(内部使用写时复制机制) - 写多读少:
Collections.synchronizedList(new ArrayList<>())
+合理锁粒度
- 读多写少:
相关问答FAQs
Q1: 什么时候应该选择ArrayList
而不是LinkedList
?
A: 当业务场景以随机访问为主时(如根据ID查询订单),ArrayList
的O(1)时间复杂度优势明显;若主要进行频繁的插入/删除操作(如日志队列),则应选择LinkedList
,实测数据显示,在百万级数据量下,ArrayList
的随机访问速度比LinkedList
快约3个数量级。
Q2: 为什么遍历ArrayList
时修改会抛出ConcurrentModificationException
?
A: 这是Java集合框架的快速失败机制(Fail-Fast)。ArrayList
内部维护了一个modCount
计数器,每次结构性修改(增删改)都会递增该值,迭代器的checkForComodification()
方法会在每次next()操作前检查预期修改次数是否匹配实际修改次数,若检测到不一致(即遍历过程中发生了结构性修改),立即抛出异常,这种设计是为了及时发现并发修改问题,保证数据一致性,解决方案包括:①使用迭代器的remove()
方法;②改用for
循环倒序删除;③克隆新