错误在于循环条件应为 i
用
for循环配合size()遍历List时,下标越界是高频错误直接写
for (int i = 0; i 会导致 <code>IndexOutOfBoundsException—— 因为合法下标范围是[0, size() - 1],而多跑了一次。Java 的 <code>List是零起点索引,这点和 Python 不同,别被惯性带偏。正确写法必须是:
i 。常见业务中还要注意:如果循环里会删元素,<code>size()动态变化,但i还在递增,极易跳过相邻项。
- 只读遍历:用
for (int i = 0; i 安全- 边遍历边删除:改用
Iterator.remove()或倒序for (int i = list.size() - 1; i >= 0; i--)- 需要索引 + 元素值:优先考虑增强
for(for (Type item : list))+ 单独计数器,更简洁不易错
size()返回的是当前长度,不是容量,且不保证线程安全
ArrayList.size()是 O(1) 操作,它只是返回内部elementData数组中实际存了多少个元素;LinkedList.size()在 Java 8+ 也是 O(1),因为维护了size字段。但要注意:这个值不反映底层数组容量(ArrayList的capacity),也不能用于判断是否“满”——size() == capacity才会触发扩容。多线程环境下,如果多个线程同时修改
List,size()可能返回过期值。比如 A 线程刚调完add(),B 线程立刻读size(),可能还是旧值(除非用Collections.synchronizedList()或CopyOnWriteArrayList)。
- 单线程场景:放心用
size()控制循环边界- 并发修改场景:避免依赖
size()做条件判断,改用isEmpty()或同步块包裹- 性能敏感循环:不要在每次迭代里重复调
list.size(),提前缓存:int len = list.size(); for (int i = 0; i增强
for和传统for+size()的适用边界很明确增强
for(for (E e : list))本质是语法糖,编译后仍用Iterator。它不能直接拿到索引,也不支持在循环中调list.remove(i)(会抛ConcurrentModificationException)。所以当业务需要索引位置(比如日志打桩标序号、批量更新第 N 条)、或需根据索引做分支逻辑(如奇偶处理、首尾特殊逻辑),就必须用传统for+size()。
- 只要取元素值、不做索引相关操作 → 用增强
for,代码干净、不易越界- 要改元素(如
list.set(i, newValue))、要索引参与计算、要按位置分批 → 用for (int i = 0; i- 要删元素且依赖索引条件(如“删掉所有索引为偶数的字符串”)→ 倒序循环,避免下标偏移
遍历中修改
List导致的ConcurrentModificationException不是“并发”问题这个异常名有误导性:即使单线程,只要用增强
for或Iterator遍历时,直接调list.remove()或list.add(),就会触发它。因为ArrayList内部有modCount计数器,Iterator创建时记下初值,每次调next()都校验是否被外部修改过。解决方法不是加锁,而是选对 API:
- 删元素:用
iterator.remove()(安全,且只能删当前项)- 批量删满足条件的项:用
removeIf(predicate)(Java 8+,内部已处理modCount)- 必须用索引删:用倒序
for,或收集待删索引再统一删(list.removeAll(toRemove))真正容易被忽略的是:有些工具类方法(如
list.subList().clear())也会触发这个异常,因为它底层仍是结构修改。遇到ConcurrentModificationException,先看是不是在遍历中动了原List的结构,而不是急着上synchronized。











