Java这门现代语言为程序员提供了很多便利的开发手段。具体就循环而言,除开从C和C++继承而来的传统for写法,也提供了在很多时候(包括在解答AP计算机科学主观题)更为方便的for-each语句。

比如对于一个String的数组,传统上遍历每一个元素需要这么写:

String[] fruits = new String[] { "Orange", "Apple", "Pear", "Strawberry" };

for (int i = 0; i < fruits.length; i++) {
    String fruit = fruits[i];
    // fruit is an element of the `fruits` array.
}

但这个可以等价于更加简洁的for-each语句:

String[] fruits = new String[] { "Orange", "Apple", "Pear", "Strawberry" };

for (String fruit : fruits) {
    // fruit is an element of the `fruits` array.
}

for-each对于AP计算机科学所考察的List也同样适用。所以

List someList = new ArrayList();
// add "monkey", "donkey", "skeleton key" to someList

for (String item : someList) {
    System.out.println(item);
}

事实上等价于

List someList = new ArrayList();
// add "monkey", "donkey", "skeleton key" to someList

for (Iterator i = someList.iterator(); i.hasNext();) {
    String item = i.next();
    System.out.println(item);
}

注意for-each的括号中只有两个部分:前面的是一个自定义的变量,其作用域仅限于for之后的花括号中,用于遍历数组或集合中的每个元素;后面则是数组或集合的名字,注意数组或集合所包含的数据类型需要和前面的变量一致,否则会出错。两者之间通过一个冒号分隔。

for-each的好处在于写法更加简洁优雅,避免了对于索引或者Iterator的直接操作,也确保了数组或集合中的每个元素都会被读取。

但是for-each同时也带来了以下这些问题:

  • for-each循环无法修改数组的内容,也无法改变集合的结构

以下的示例代码对数组本身不会有任何影响,num只是一个一次性的局部变量。

for (int num : marks) {
    // only changes num, not the array element
    num = num*2; 
}

在for-each中调用所循环Listadd()remove() method可能会抛出java.util.ConcurrentModificationException异常。

  • for-each循环无法读取索引或Iterator

如果希望寻找某些满足特定条件的元素并返回索引或Iterator,for-each不是一个理想的选择。

for (int num : numbers) { 
    if (num == target) 
    {
        return ???;   // do not know the index of num
    }
}

同理,for-each在同一层循环里一般只能处理一个数组或集合。

// cannot be easily converted to a for-each loop 
for (int i=0; i<numbers.length; i++) {
    if (numbers[i] == arr[i]) 
    { ...
    } 
}
  • for-each循环的顺序无法被调整,也必定访问所有元素

对于逆序、部分、非标准间隔或者特殊的循环条件,for-each无法处理。

// cannot be converted to a for-each loop
for (int i=numbers.length-1; i>0; i--) 
{
      System.out.println(numbers[i]);
}

在什么情况下使用for-each?简单遍历、仅读取元素是一条可用的判据。


陈 欣

AADPS创始人

发表评论