for循环遍历List时下标访问快,但ArrayList支持O(1)随机访问,LinkedList需O(n)遍历查找,性能差异显著。

for循环遍历List:下标访问快,但ArrayList和LinkedList表现天差地别
直接用for (int i = 0; i 遍历,对<code>ArrayList是O(1)随机访问,速度最快;但对LinkedList每次get(i)都要从头遍历到第i个节点,整体变成O(n²),实际跑起来可能慢10倍以上。
常见错误现象:LinkedList在大集合上用普通for循环,CPU打满、响应明显卡顿,日志里却看不到异常。
- 只在确定是
ArrayList(或CopyOnWriteArrayList等支持高效随机访问的实现)时才用普通for - 不确定具体实现类时,别碰
list.get(i)——哪怕只是想取第一个元素 - 如果必须用下标且类型未知,先用
instanceof判断:if (list instanceof RandomAccess)再选策略
增强for循环(for-each):语法简洁,底层全靠迭代器,安全但不透明
写成for (String s : list),看着干净,但编译后完全等价于显式调用iterator()。它自动处理了hasNext()和next(),也默认启用fail-fast机制——集合被并发修改就抛ConcurrentModificationException。
使用场景:读多写少、不关心索引、不需要中途移除元素。
立即学习“Java免费学习笔记(深入)”;
- 不能在循环体里调用
list.remove(),会触发fail-fast(哪怕你删的是前一个元素) - 想边遍历边删,必须用
Iterator.remove(),增强for做不到 - 对
ArrayList和LinkedList性能差异不大,都走迭代器,但比普通for遍历LinkedList快得多
显式Iterator:唯一能安全删除的遍历方式
需要边看边删时,只有Iterator的remove()方法是安全的。它把删除逻辑封装在迭代器内部,能同步更新内部游标和modCount,避开ConcurrentModificationException。
错误写法:for (String s : list) { if (s.isEmpty()) list.remove(s); }——100%报错。
- 正确姿势:
Iterator<string> it = list.iterator(); while (it.hasNext()) { String s = it.next(); if (s.isEmpty()) it.remove(); }</string> -
it.remove()只能调用一次,且必须在next()之后,否则抛IllegalStateException - 注意:
CopyOnWriteArrayList的迭代器remove()是空操作,不生效——它设计就是写时复制,删不了当前快照里的元素
Stream.forEach():别把它当遍历工具用
list.stream().forEach(...)看起来像新式遍历,但它本质是创建流管道+触发终端操作,有对象分配开销和额外函数调用。小数据集看不出差别,但高频循环(比如每秒万次)下,比传统for慢2–5倍。
更关键的是:它不保证顺序(除非用forEachOrdered),且无法安全修改原集合——lambda里调list.remove()照样触发并发修改异常。
- 真要用Stream,目的应该是转换、过滤、聚合,而不是单纯“走一遍”
- 需要顺序执行且不想写样板代码,优先考虑
list.forEach()(集合自带的默认方法),它比stream().forEach()轻量得多 - 千万别为了“显得现代”把简单遍历改成Stream,尤其是嵌套循环里
Arrays.asList()返回的不可扩容列表、MyBatis查出来的Page包装类、Spring的MutablePropertyValues……这些看着像List,随机访问或迭代行为可能完全不符合直觉。










