# 19. 如何使用 subList 方法高效拆分 List?有哪些注意事项?

# 标准答案

subList(int fromIndex, int toIndex)List 接口提供的视图操作,用于在常数时间内获取原 List 的一个子列表
优点:

  • 高效subList() 不复制数据,而是基于原列表的视图(View),避免额外的内存开销
  • 适用于大列表拆分:尤其在分页、批量处理等场景,减少数据复制的开销

注意事项:

  • 子列表依赖原列表,修改 subList 会影响原 List,反之亦然
  • 删除 subList 后,原 List 可能报 ConcurrentModificationException
  • 适用于只读或短期操作,避免长时间持有 subList 造成的内存泄漏

# 答案解析

# 1. subList() 的底层原理

subList() 不会创建新的列表对象,而是返回 AbstractList#SubList 视图,它仅维护:

  • offset(偏移量):起始索引 fromIndex
  • size(子列表大小)toIndex - fromIndex
  • List 的引用:子列表依赖原 List 进行操作

示例代码:

List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
List<Integer> subList = list.subList(2, 6); // [3, 4, 5, 6]
System.out.println(subList); 
subList.set(0, 99); 
System.out.println(list); // 原列表同步修改 -> [1, 2, 99, 4, 5, 6, 7, 8, 9, 10]
1
2
3
4
5

subList() 不复制数据,而是直接修改原 List

# 2. subList() 高效拆分 List

在大规模数据处理中,subList() 可用于分页、并行计算等高效拆分策略:

public static <T> List<List<T>> splitList(List<T> list, int batchSize) {
    List<List<T>> result = new ArrayList<>();
    int size = list.size();
    for (int i = 0; i < size; i += batchSize) {
        result.add(list.subList(i, Math.min(size, i + batchSize))); // 高效切分
    }
    return result;
}
1
2
3
4
5
6
7
8

示例

List<Integer> data = IntStream.range(1, 21).boxed().collect(Collectors.toList());
List<List<Integer>> partitions = splitList(data, 5);
System.out.println(partitions); // [[1,2,3,4,5], [6,7,8,9,10], ...]
1
2
3

避免 for 循环 addAll() 拷贝数据,提高性能!

# 3. subList() 的常见坑

# (1)修改 subList 会影响原列表
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> subList = list.subList(1, 4);
subList.set(0, 99); 
System.out.println(list); // [1, 99, 3, 4, 5]
1
2
3
4

subList 修改同步影响 list,易导致意外 BUG!

# (2)subList 删除元素后,原 List 可能报错
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> subList = list.subList(1, 4);
subList.clear(); 
System.out.println(list); // [1, 5]
System.out.println(subList.size()); // 0
1
2
3
4
5

⚠️ subList.clear() 直接修改 list,子列表也会失效!

# (3)不能直接转换为 ArrayList
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
List<Integer> subList = list.subList(1, 4);
List<Integer> copy = new ArrayList<>(subList); // 复制一份,避免原列表修改影响
1
2
3

⚠️ 需要 new ArrayList<>(subList) 复制,避免 ConcurrentModificationException

# 4. subList() 与其他拆分方法对比

方法 是否拷贝数据 线程安全 适用场景
subList() ❌ 仅创建视图 ❌ 受原 List 影响 高效分页,批处理
List#stream().skip().limit() ✅ 重新生成流 流式计算,分页
for循环 + addAll() ✅ 复制数据 需独立列表副本

# 深入追问

  • 为什么 subList()LinkedList 上性能较差?
  • 如何保证 subList() 操作时不会影响原 List
  • subList()CopyOnWriteArrayList 这种线程安全 List 上是否适用?

# 相关面试题

  • subList()stream().skip().limit() 该如何选择?
  • 多线程环境 下,如何安全地拆分 List
  • subList() 为什么可能引发 ConcurrentModificationException