arraylist.sublist()返回原列表的视图而非副本,共享底层数据但有独立边界检查;越界访问触发indexoutofboundsexception,原列表结构性修改则触发concurrentmodificationexception。

Java 中 ArrayList.subList() 返回的是原列表的视图(view),不是独立副本。这意味着它共享底层数据,但有自己的边界检查逻辑。越界访问或在 subList 存在期间修改原 List 结构,都会触发 IndexOutOfBoundsException 或 ConcurrentModificationException —— 但它们的触发时机和原因不同,容易混淆。
subList 自身越界:索引超出视图范围
调用 subList(fromIndex, toIndex) 时,参数必须满足:0 ≤ fromIndex ≤ toIndex ≤ size()。只要违反这个条件,立刻抛出 IndexOutOfBoundsException,哪怕原 List 没被修改。
-
list.subList(2, 10)在原 list 只有 5 个元素时会失败(10 > 5) -
list.subList(-1, 3)直接因负起始索引报错 -
list.subList(0, list.size() + 1)因右边界超限失败
通过 subList 修改引发越界
subList 支持 add()、remove()、set() 等操作,但所有索引都相对于该视图自身长度计算。例如:
- 原 list = [a, b, c, d, e],
sub = list.subList(1, 4)→ 视图为 [b, c, d],长度为 3 -
sub.get(3)报错:视图里只有索引 0~2,3 越界 -
sub.add("x")合法,原 list 变成 [a, b, c, d, x, e],sub 视图变成 [b, c, d, x]
原 List 结构修改导致 subList 失效
subList 内部维护了对原 list 的引用,并记录了“期望的 modCount”。一旦原 list 被结构性修改(如 add()、remove()、clear()),其 modCount 变化,后续对 subList 的任何操作(包括 size()、get()、iterator())都会立即抛 ConcurrentModificationException —— 这不是 IndexOutOfBoundsException,但常被误认为是越界。
- 创建
sub = list.subList(0, 2)后,执行list.add("new") - 再调用
sub.size()就会失败,因为 modCount 不匹配 - 注意:
list.set(0, "x")是非结构性修改,不会触发此异常
安全使用 subList 的建议
避免运行时异常的关键是明确 subList 的生命周期和作用域:
- 需要独立副本?用
new ArrayList(list.subList(i, j))构造新对象 - 只读场景?可直接用 subList,但确保原 list 不被结构修改
- 需在 subList 上增删?优先考虑是否真需要视图语义;否则直接操作原 list 并手动计算索引
- 调试时注意异常堆栈:IndexOutOfBoundsException 来自索引计算错误;ConcurrentModificationException 来自 modCount 不一致
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











