一、List 接口概述
1. 基本定义
- 继承关系:
List
是 Java 集合框架(Collection Framework)中的一个有序队列接口,直接继承自Collection
接口。 - 核心特性:
- 有序性:元素按插入顺序存储,可通过索引(Index)访问元素。
- 可重复性:允许存储重复元素。
- 动态扩容:底层实现类(如
ArrayList
、LinkedList
)支持动态调整容量。
2. 与其他接口的区别
接口 | 有序性 | 重复性 | 数据结构 | 典型实现类 |
---|---|---|---|---|
List | 是 | 是 | 线性表 | ArrayList 、LinkedList |
Set | 否 | 否 | 集合(哈希表等) | HashSet 、TreeSet |
Queue | 是 | 是 | 队列(FIFO) | LinkedList 、PriorityQueue |
二、List 接口核心方法
1. 元素操作方法
(1)添加元素
boolean add(E e)
:向列表末尾添加元素,成功返回true
。
List<String> list = new ArrayList<>();
list.add("Apple"); // 添加到末尾
void add(int index, E element)
:在指定索引处插入元素,后续元素后移。
list.add(1, "Banana"); // 在索引1处插入元素
(2)删除元素
E remove(int index)
:移除指定索引处的元素,返回被删除元素。
String removed = list.remove(0); // 移除索引0的元素
boolean remove(Object o)
:移除列表中第一个匹配的元素(通过 equals
方法判断)。
list.remove("Apple"); // 移除值为"Apple"的元素
(3)修改元素
E set(int index, E element)
:用指定元素替换指定索引处的元素,返回旧元素。
String oldValue = list.set(0, "Grape"); // 将索引0的元素改为"Grape"
(4)查询元素
E get(int index)
:返回指定索引处的元素。
String first = list.get(0); // 获取索引0的元素
int indexOf(Object o)
:返回元素首次出现的索引,不存在则返回 -1
。
int pos = list.indexOf("Banana"); // 查询"Banana"的位置
int lastIndexOf(Object o)
:返回元素最后一次出现的索引,不存在则返回-1
。
2. 集合操作方法
boolean contains(Object o)
:判断列表是否包含指定元素(基于equals
)。boolean.addAll(Collection<? extends E> c)
:将其他集合的所有元素添加到列表末尾。void clear()
:移除列表中的所有元素。boolean isEmpty()
:判断列表是否为空。int size()
:返回列表中的元素个数。
3. 迭代与视图方法
- 迭代器(Iterator):
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();
// 处理元素
}
列表迭代器(ListIterator):支持双向遍历和元素修改:
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String element = lit.next();
lit.set(element + "_modified"); // 修改元素
}
- 子列表(SubList):返回列表中指定范围的视图(
List.subList(int fromIndex, int toIndex)
):
List<String> sub = list.subList(0, 2); // 包含索引0,不包含索引2
三、List 接口的实现类
1. ArrayList
(1)特点
- 底层结构:动态数组(
Object[]
)。 - 优缺点:
- ✅ 随机访问(
get(int)
)速度快(时间复杂度 O (1))。 - ❌ 插入 / 删除元素时需移动后续元素,效率较低(时间复杂度 O (n))。
- ✅ 随机访问(
- 适用场景:频繁查询、较少增删的场景(如数据展示、日志记录)。
(2)扩容机制
- 初始容量默认值为
10
(JDK 1.8)。 - 当元素数量超过容量时,新容量为 原容量的 1.5 倍(通过
Arrays.copyOf
复制数组)。
2. LinkedList
(1)特点
- 底层结构:双向链表(每个节点包含
prev
、next
指针)。 - 优缺点:
- ✅ 插入 / 删除元素时只需修改指针,效率高(时间复杂度 O (1),若已知节点位置)。
- ❌ 随机访问需遍历链表,效率低(时间复杂度 O (n))。
- 适用场景:频繁增删的场景(如队列、栈、缓存)。
(2)特殊方法
- 可作为队列(Queue)使用:
LinkedList<String> queue = new LinkedList<>();
queue.offer("A"); // 入队(添加到末尾)
String first = queue.poll(); // 出队(移除并返回头部元素)
可作为栈(Stack)使用:
queue.push("B"); // 入栈(添加到头部)
String last = queue.pop(); // 出栈(移除并返回头部元素)
3. Vector(线程安全)
(1)特点
- 底层结构:动态数组,与
ArrayList
类似。 - 线程安全:方法通过
synchronized
修饰(如add()
、get()
),但性能较低。 - 适用场景:多线程环境下需要线程安全的场景(较冷门,推荐使用
Collections.synchronizedList()
替代)。
4. 实现类对比表
实现类 | 底层结构 | 线程安全 | 随机访问效率 | 增删效率(中间位置) | 默认初始容量 |
---|---|---|---|---|---|
ArrayList | 动态数组 | 否 | 高(O (1)) | 低(O (n)) | 10 |
LinkedList | 双向链表 | 否 | 低(O (n)) | 高(O (1)) | - |
Vector | 动态数组 | 是 | 高(O (1)) | 低(O (n)) | 10 |
四、List 接口的常用操作场景
1. 遍历列表的 4 种方式
(1)普通 for 循环(索引遍历)
for (int i = 0; i < list.size(); i++) {
String element = list.get(i);
}
适用场景:需要通过索引操作元素(如修改、删除)。
(2)增强 for 循环(foreach)
for (String element : list) {
// 只读操作元素
}
注意:遍历时若修改列表(如 list.remove()
),会抛出 ConcurrentModificationException
(fail-fast 机制)。
(3)Iterator 迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();
if (element.equals("Apple")) {
it.remove(); // 安全删除元素的方式
}
}
优势:支持遍历过程中安全删除元素(通过 Iterator.remove()
)。
(4)Java 8 Stream 流式遍历
list.stream().forEach(element -> {
System.out.println(element);
});
适用场景:结合 Lambda 表达式实现复杂逻辑(如过滤、映射)。
2. 列表排序与搜索
(1)排序(Collections.sort()
)
List<Integer> nums = Arrays.asList(3, 1, 4, 2);
Collections.sort(nums); // 升序排列(默认自然排序)
// 自定义排序(降序)
Collections.sort(nums, (a, b) -> b - a);
(2)二分查找(Collections.binarySearch()
)
Collections.sort(nums); // 需先排序
int index = Collections.binarySearch(nums, 3); // 返回元素3的索引
3. 线程安全处理
- 场景:多线程环境下操作
List
时,需保证线程安全。 - 解决方案:
- 使用
Vector
(性能较低)。 - 通过
Collections.synchronizedList()
包装普通List
:
- 使用
List<String> safeList = Collections.synchronizedList(new ArrayList<>());
使用并发包 java.util.concurrent
中的 CopyOnWriteArrayList
(适用于读多写少场景):
List<String> concurrentList = new CopyOnWriteArrayList<>();
五、常见问题与最佳实践
1. ArrayList vs LinkedList 如何选择?
- 优先选 ArrayList:需要频繁随机访问(如查询),或数据量已知、增删操作少。
- 优先选 LinkedList:需要频繁在头部 / 中间插入 / 删除元素,或数据量不确定、需高效动态调整。
2. 列表扩容的性能影响
ArrayList
扩容时会创建新数组并复制旧元素,若初始容量预估不足,多次扩容会导致性能下降。- 最佳实践:通过构造方法指定初始容量:
List<String> list = new ArrayList<>(100); // 初始容量设为100,减少扩容次数
3. 避免内存泄漏
- 若存储大量对象,需及时调用
clear()
或置为null
,帮助垃圾回收器回收内存。 - 强制类型转换风险:非泛型
List
会存储Object
类型,取出时需强制类型转换,可能引发ClassCastException
。 - 最佳实践:使用泛型限定元素类型:
List<String> list = new ArrayList<>(); // 限定只能存储String类型
六、总结
特性 | List 接口特点 |
---|---|
核心优势 | 有序性、可重复性、索引访问,适用于需要按顺序操作的数据场景。 |
实现类选择 | - 高频查询:ArrayList - 高频增删: LinkedList - 线程安全: CopyOnWriteArrayList |
常用操作 | 添加 / 删除元素、遍历、排序、子列表操作、线程安全处理。 |
通过掌握 List
接口的核心方法与实现类特性,可在开发中高效处理线性数据集合,提升代码的健壮性与性能。