sublist返回的是原列表的动态视图而非新列表,原列表结构修改会导致所有sublist实例失效并抛concurrentmodificationexception;需独立副本时应显式拷贝。

subList 返回的是视图,不是新列表
调用 list.subList(fromIndex, toIndex) 不会复制元素,而是返回一个绑定原 ArrayList(或 LinkedList)内部状态的动态视图。这意味着:只要原列表结构被修改(增、删、清空),所有已存在的 subList 实例立刻失效,后续任何操作都可能抛出 ConcurrentModificationException —— 即便你没开多线程。
常见错误现象:
• 用 subList 切出一段数据后,对原列表调用 add() 或 remove()
• 然后访问该 subList 的 size()、get(0)、甚至 iterator().hasNext(),直接崩溃
• 错误信息是 java.util.ConcurrentModificationException,但根本没并发——纯单线程也崩
- 只读场景下可放心用
subList,但必须确保原列表在整个生命周期内“结构不变”(允许set(),不允许增删) - 若需独立副本,必须显式拷贝:
new ArrayList(list.subList(a, b))或list.subList(a, b).stream().toList()(Java 16+) -
subList对CopyOnWriteArrayList无效——它不支持subList,调用会抛UnsupportedOperationException
迭代 subList 时原列表被修改的典型陷阱
这是最隐蔽的坑:你以为在遍历子列表,其实底层仍依赖原列表的 modCount。哪怕只是在循环体里删了原列表第一个元素,下一次 next() 就炸。
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
List<String> sub = list.subList(1, 3); // ["b", "c"]
for (String s : sub) {
System.out.println(s);
list.remove(0); // ⚠️ 这里改了原列表结构
}
运行直接抛 ConcurrentModificationException。因为 sub 的迭代器和 list 共享同一个修改计数器。
- 避免在遍历
subList期间修改原列表任何结构性方法(add()、remove()、clear()、retainAll()等) - 如果必须边遍历边删,先转成独立列表:
List<string> safeSub = new ArrayList(sub)</string>,再遍历safeSub -
subList的forEach()、stream()同样受此约束——它们底层仍是基于原列表的迭代器
subList 在多线程环境下的双重风险
单线程已够呛,多线程下问题更重:不仅有结构修改冲突,还有可见性问题。即使你用 synchronized 锁住原列表,subList 视图本身不是线程安全的封装,它的字段(如 parent、offset)没有 volatile 保护。
- 两个线程分别持有同一
subList实例,一个调get(),另一个在原列表上add()→ 必现ConcurrentModificationException - 即使所有操作都加锁,
subList的size()可能返回过期值(因未同步读取parent.size()) - 正确做法:要么全程用
Collections.synchronizedList()包装原列表(但subList仍需额外同步),要么直接用不可变副本:ImmutableList.copyOf(list.subList(a, b))(Guava)或List.copyOf(list.subList(a, b))(Java 10+)
替代方案选型:什么时候该放弃 subList
如果你需要切片结果具备“隔离性”“可变性”或“线程安全性”,subList 就不该是默认选项。它本质是个轻量级视图,设计初衷是临时、只读、短生命周期使用。
- 需要后续增删元素?→ 用
new ArrayList(list.subList(a, b)) - 要传给其他模块且不确定对方是否修改原列表?→ 强制拷贝,别省那点内存
- 做配置切片、日志分段等长期持有的场景?→ 用
List.copyOf()(不可变)或明确构造新列表 - 性能敏感且确定只读?
subList确实零拷贝,但得确保调用链上没人碰原列表——这点比性能更难保障
真正容易被忽略的点:subList 的 equals() 和 hashCode() 依赖原列表内容,一旦原列表变化,之前存进 HashSet 的 subList 可能再也找不到了。










