subList是原ArrayList的动态视图,共享modCount,原列表结构性修改会触发ConcurrentModificationException;仅允许get/set/size等读操作,禁止add/remove/clear等结构性操作;安全用法是只读使用或立即转为独立副本。

修改原列表会让子列表失效,大概率抛 ConcurrentModificationException 异常——这不是 bug,而是 subList 设计决定的。
为什么一改原列表就出错?
subList 返回的是原 ArrayList 的动态视图,不是副本。它和原列表共享同一个 modCount(修改计数器)。只要原列表执行了 add、remove、clear 等结构性操作,modCount 就会加 1;此时再访问子列表(比如调用 get()、size()、forEach() 或进入 for-each 循环),就会触发 fail-fast 检查,立即报 ConcurrentModificationException。
- 哪怕只是在子列表范围外添加一个元素,也会触发异常
- 哪怕子列表只读,只要原列表被改过,后续任何操作都可能失败
- Stream 处理(如
subList.stream().map(...).collect(...))同样依赖迭代器,一样中招
哪些修改会直接“传染”到子列表?
对子列表做 set(i, e)、get(i)、size() 是允许的,且会实时反映到原列表对应位置:
-
subList.set(0, "x")→ 原列表中对应索引位置的元素被替换 -
subList.get(2)→ 实际读取的是原列表fromIndex + 2处的值 - 这种同步是双向的:你改原列表该位置,子列表读出来也变
哪些操作子列表根本不支持?
subList 实现类(如 ArrayList.SubList)禁止结构性修改,调用以下方法会直接抛 UnsupportedOperationException:
立即学习“Java免费学习笔记(深入)”;
-
add(e)、add(index, e) -
remove(index)、remove(e)、clear() -
addAll(...)、retainAll(...)、removeAll(...)
注意:个别 JDK 版本或实现下 remove(index) 可能看似成功,实则是委托给原列表执行,导致索引偏移、后续访问越界或静默异常,不建议依赖。
怎么安全地用 subList?
核心原则:视图只读 + 避免原列表变动,或立刻转成独立副本。
- 纯读取场景(如分页展示、临时遍历):直接用 subList,轻量无开销
- 需要增删改:马上创建新 ArrayList ——
new ArrayList(list.subList(from, to)) - 只需不可变副本(Java 10+):用
List.copyOf(list.subList(from, to)) - 避免长期持有 subList 对象:尤其别放进缓存、静态字段或跨线程传递,否则可能阻塞原列表 GC


















