本文比较了java传统for循环及增强for循环的优缺点,及适合的使用场景。
首先来看一段代码片段:
片段:遍历数组
// 生成一个长度为10的整型数组
int[] numArr = new int[10];
// 传统for循环,遍历赋值
for( int i=0; i<numArr.length; i++ ) {
numArr[i] = i;
}
// 增强for循环,遍历取值
for( int a : numArr ) {
System.out.println( a );
}
上述代码在对数组赋值时采用了传统for循环,而在输出时采用了增强for循环(以下称为foreach循环)。
foreach循环中的":“可以理解为"在…的里面”,读作对于某集合中的每个元素。最大的差别在于,用foreach循环时无需考虑边界问题,并且边界值只会被计算一次。而在传统for循环中(i<numArr.length),边界值判定则会被多次执行。相较于传统for循环,除了代码简洁外,还有略微的性能优势。因此,应该尽可能地使用foreach循环,但在以下2种场景中无法使用:
- 过滤——如果需要遍历集合,并删除选定的元素,就需要使用显式的迭代器,以便可以调用它的remove方法。
- 转换——如果需要遍历列表或数组,并取代它部分或者全部的元素值,就需要列表迭代器或者数组索引,以便设定元素的值。
场景1示例:
package javaTest;
import java.util.ArrayList;
import java.util.List;
public class CollectionTest {
public static void main(String args[]) {
List<String> lstStr = new ArrayList<>();
// 生成一个存放String类型的列表,并存入三个字符串("test1","test2","test3")
for( int i = 0; i < 3; i++ ) {
lstStr.add( "test" + String.valueOf( i + 1 ) );
}
// 确认List中内容是否为("test1","test2","test3")
for( String s : lstStr) {
System.out.println( s );
}
// 现某需求为,字符串等于"test2"时,从List中删除
for( int i = 0; i < lstStr.size(); i++ ) {
if( lstStr.get( i ) != null && lstStr.get( i ).equals( "test2" ) ){
lstStr.remove( i );
}
}
// 确认List中内容是否为("test1","test3")
for( String s : lstStr) {
System.out.println( s );
}
}
}
执行结果:
场景2示例:参考开头的代码片段,对数组赋值的for循环。以及场景1中的赋值片段,在foreach循环中如果需要获取index需要额外定义,因此建议使用传统for循环。
而在可使用foreach循环的情况时,代码会变得非常整洁。这个优势在遍历集合和嵌套循环中,更为明显。
对比示例(分别利用传统for循环和增强for循环生成扑克牌):
为了方便起见,所有枚举型和扑克牌类都在同一文件中定义。
Suit和Rank为枚举型,代表花色和大小。
两个for循环生成的结果相同,都为
花色:CLUB 大小:ACE
…
花色:CLUB 大小:KING
花色:DIAMOND 大小:ACE
…
花色:DIAMOND大小:KING
花色:HEART大小:ACE
…
花色:HEART大小:KING
花色:SPADE大小:ACE
…
花色:SPADE大小:KING
package javaTest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
// 花色枚举型(梅花,方片,红桃,黑桃)
enum Suit { CLUB,DIAMOND,HEART,SPADE };
// 大小枚举型(A-K)
enum Rank { ACE,DEUCE,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN,JACK,QUEEN,KING };
// 卡牌类
class Card{
private Suit suit;
private Rank rank;
// 构造方法创建Card实体
public Card( Suit suit, Rank rank ) {
this.suit = suit;
this.rank = rank;
}
// 构建器方法创建Card实体
public static class Builder{
private Suit suit;
private Rank rank;
public Builder( Suit suit, Rank rank ) {
this.suit = suit;
this.rank = rank;
}
public Card build() {
return new Card(this);
}
}
public Card(Builder builder) {
suit = builder.suit;
rank = builder.rank;
}
// 覆盖toString
@Override
public String toString() {
return "花色:" + suit.toString() + " 大小:" + rank.toString();
}
}
public class ForTest {
public static void main( String args[] ) {
Collection<Suit> suits = Arrays.asList( Suit.values() );
Collection<Rank> ranks = Arrays.asList( Rank.values() );
List<Card> deck1 = new ArrayList<Card>();
List<Card> deck2 = new ArrayList<Card>();
//传统for循环生成扑克牌
for( Iterator<Suit> s = suits.iterator(); s.hasNext(); ) {
Suit suit = s.next();
for( Iterator<Rank> r = ranks.iterator(); r.hasNext(); ) {
Rank rank = r.next();
Card card = new Card( suit, rank );
System.out.println( "传统for循环生成扑克---" + card );
deck1.add( card );
}
}
// 增强for循环生成扑克牌
for( Suit suit : suits ) {
for( Rank rank : ranks ) {
Card card = new Card.Builder(suit, rank).build();
System.out.println( "增强for循环生成扑克---" + card );
deck2.add( card );
}
}
}
}
外层循环中,遍历梅花,方片,红桃,黑桃四个花色,内层循环中遍历A到K的卡牌大小。需要注意的是,在传统for循环中,必须在外部循环的作用域中添加一个变量来保存外部元素。此例中为扑克牌花色。
总结
增强for循环(foreach)优势:
- 代码整洁
- 无需考虑边界问题,性能略微提升
- 预防Bug
增强for循环(foreach)使用场景
- 对遍历原始集合不做修改的情况