两种List遍历for循环写法的性能差异及JVM优化问题
两种List遍历for循环的区别、性能与JVM优化解析
首先先修正下你代码里的小疏漏——两种写法里的list.size都应该是list.size()(调用方法,不是直接访问字段),先把正确的写法列出来:
- 写法1:
for(int i=0, length=list.size(); i<length; i++) - 写法2:
for(int i=0; i<list.size(); i++)
核心逻辑区别
这两种写法最本质的差异在于list.size()的调用次数:
- 写法1会在循环开始前,一次性调用
list.size()并把结果存在局部变量length里,整个循环过程中不会再调用size()方法。 - 写法2会在每次循环的条件判断阶段都调用一次
list.size(),也就是循环迭代多少次,size()就被调用多少次。
这个差异会直接影响两种写法的行为:
- 如果循环过程中List的元素数量不会变化(比如用ArrayList且循环内没有add/remove操作),那两种写法逻辑完全等价,遍历的结果一致。
- 如果循环过程中List的元素数量会动态改变(比如循环里有删除元素的操作),写法2会每次取最新的size,而写法1用的是循环开始时的初始size。举个例子:如果循环里删除了元素,写法2会因为size变小提前结束循环,而写法1可能会继续循环到初始的length,导致出现
IndexOutOfBoundsException。
性能差异分析
性能差异取决于list.size()方法的实现:
- 对于Java标准库中的大多数List(比如ArrayList、LinkedList),
size()是O(1)操作——内部维护了一个专门的size变量,调用时直接返回这个变量的值,几乎没有开销。这种情况下两种写法的性能几乎没差别,哪怕循环个几十万次,差距也微乎其微。 - 但如果是自定义的List实现,或者某些特殊的List(比如需要实时计算元素数量的包装类),
size()是O(n)操作(比如每次都要遍历整个集合统计元素),那写法1的性能会碾压写法2——写法1只计算一次size,时间复杂度是O(n);写法2每次循环都要计算size,时间复杂度会变成O(n²),数据量大的时候差距会非常明显。
JVM会做优化吗?
HotSpot这类主流JVM的JIT(即时编译器)会做循环不变量消除优化——如果JVM能确定list.size()在循环过程中不会发生变化(比如List是局部变量,循环内没有修改List的操作,也没有其他线程可能修改它),那么JVM会自动把list.size()的调用提前到循环外面,相当于把写法2优化成写法1的样子。
但这个优化不是100%会生效:
- 如果List是全局变量、或者被其他线程引用、或者循环内有可能修改List的操作(哪怕实际没执行),JVM就无法确定size是否会变化,也就不会做这个优化。
- 对于那些
size()是O(n)的自定义List,JVM通常也无法识别到它的耗时,所以不会主动优化,这时候还是得手动用写法1来提升性能。
总结
- 当List在循环中不会被修改时,两种写法逻辑等价,性能在JVM优化后几乎一致;
- 当List在循环中会动态变化时,两种写法的行为完全不同,需要根据业务需求选择;
- 当
size()是耗时操作时,写法1的性能更优,且JVM不一定能自动优化写法2,建议手动提前缓存size值。
内容的提问来源于stack exchange,提问作者lian chen
相关产品推荐
相关产品推荐

