sublist 返回的是共享底层存储的活视图,非独立副本;所有写操作直接影响原列表,读操作需校验范围并避免结构性修改,隔离需求须显式拷贝。

用 subList 切片 List 本质是创建一个“窗口视图”,不复制数据、开销极小,但所有写操作都会直接作用于原集合——所谓“联动污染”不是意外,而是设计使然。防范的关键不是阻止联动,而是明确意图:只读就守边界,要隔离就主动拷贝。
认清 subList 是活视图,不是副本
调用 list.subList(from, to) 返回的子列表和原 list 共享底层存储(如 ArrayList 的 elementData 数组)。对子列表调用 set(i, x) 会改原数组对应位置;调用 add() 或 remove() 会真实增删原 list 元素,导致索引位移。这不是 bug,是委托式实现。
- 修改子列表元素 → 原 list 同步更新
- 在子列表中
add("x")→ 原 list 插入新元素,后续所有索引重排 - 遍历子列表时调用原 list 的
remove(0)→ 立即抛ConcurrentModificationException
只读切片:校验 + 遍历 + 零拷贝
若仅需分页展示、批量读取某段数据,应确保全程无结构性修改,并做基础防护:
- 提前校验范围:
if (from list.size() || from > to) throw new IllegalArgumentException(); - 用 for-each 安全遍历:
for (Item item : list.subList(from, to)) { ... }(不触发迭代器检查) - 避免在循环体内调用原 list 或子列表的
add/remove/clear - 基本类型数组不能直接用:
Arrays.asList(int[])会把整个数组当一个元素,必须改用Integer[]或IntBuffer.wrap(arr).position(from).limit(to)
需隔离操作:显式构造独立副本
一旦涉及跨线程传递、后续排序、或防止误改原数据,必须脱离视图关系:
- 构造新 ArrayList:
new ArrayList(list.subList(from, to)) - 指定容量更高效(避免扩容):
new ArrayList(Math.min(to - from, list.size())) {{ addAll(list.subList(from, to)); }} - 线程安全场景可包装为不可修改视图:
Collections.unmodifiableList(list.subList(from, to)),但注意这仍基于原 list,结构性修改后访问会失效
避开高危组合:结构性修改 + 视图共存
以下模式极易崩溃,应彻底规避:
- 一边遍历
subList,一边调用原 list 的add()、remove()、clear() - 对
Arrays.asList(基本类型数组)调用subList后尝试切片逻辑 - 假设
LinkedList.subList()支持add()或remove()(JDK 8+ 仍抛UnsupportedOperationException) - 将
subList结果长期缓存,却不控制原 list 的后续修改











