本文参考资料基于JDK1.8
,理论上也适用于之前的版本.
严格的说,for-each
是对for
的增强,本质上也属于for
循环.
for
和for-each
一般无非也就是如下三种用法,其中第三种也就是我们平时所说的for-each
:
//其中list为java.util.List类型
//第一种写法
for (int i=0, n=list.size(); i < n; i++){
list.get(i);
}
//第二种写法
for (Iterator i=list.iterator(); i.hasNext(); ){
i.next();
}
//第三种写法:增强型for循环,即for-each
for(T obj : list){
//TODO
}
经常在写for
循环的时候会很困惑:到底用哪种写法最合适.
一步步来看.
在JLS第14.14.2章节 The enhanced for statement中对for-each
的有说明,原文太长有兴趣可以点进去自行去看,大体上就是说:
- 能使用
for-each
进行循环的对象,要么是直接或间接的实现了Iterable
接口,要么就是个数组. - 如果是数组,那么在执行的时候
JVM
会解释成如下形式,即第一种写法:
for (int i=0, n=list.size(); i < n; i++){
list.get(i);
}
//特别要注意,是解释成上面这种写法,而不是下面这样的.毕竟前者的在性能上会更好一些.
for (int i=0; i < list.size(); i++){
list.get(i);
}
3.如果是实现了Iterable
接口,那么在执行的时候JVM
会解释成如下形式,即第二种写法:
for (Iterator i=list.iterator(); i.hasNext(); ){
i.next();
}
而JDK
中Iterable
接口的API
也指明了该接口是为了让实现类成为for-each
语句的目标.
鉴于在执行时JVM
会根据对象是数组还是Iterable
接口的实现类来自行决定解释成具体哪种形式的写法,因此,虽然for-each
有它的局限性,但大部分情况下,我们都可以用for-each
替代for
,毕竟for-each
可以让代码显得更简洁逻辑也更清晰.
但是,还是有例外的情况,那就是既实现了Iterable
又实现了RandomAccess
的类,比如最常见的ArrayList
.
RandomAccess
的API
中有指明,实现了RandomAccess接口的类(准确的说,是属于随机访问的List
,因为属于随机访问的List
应当实现RandomAccess
接口),在进行循环时,更适合采用第一种写法(自然也就不适合使用for-each
,因为for-each
只会判断是不是数组、是不是实现了Iterable
接口,并没有对是否实现RandomAccess
进行判断).
所以,整个的结论就是:
- 如果是数组,则采用第一种或第三种.
- 如果是实现了
Iterable
接口,并且未实现RandomAccess
接口(不是随机访问的List
),则应当使用第二种或第三种,优先使用第三种即for-each
. - 如果是实现了
Iterable
接口,并且实现了RandomAccess
接口(是随机访问的List
),则应当使用第一种.
参考资料: