Java 5 引入了一种更加简洁的 for 语法,可以用于数组和容器(这部分内容会在第 12 章和第 21 章中进一步讲解)。这种语法有时候叫作 “增强的 for”(enhanced for)。大部分文档中直接将其称为 foreach 语法,但 Java 8 里又增加了一个我们经常使用的 forEach() 方法。这样的术语使用起来容易混淆,因此我找了点儿依据,就直接称这种语法为 for-in(比如在 Python 中,实际上的语法就是 for x in sequence,所以这样的称呼是有合理的先例的)。请记住,你可能会在不同的地方看到它的不同叫法。
for-in 语句会自动为你生成每一项元素,这样你就不需要创建 int 变量来对这个元素构成的序列进行计数。假设有一个 float 数组,你想要选取该数组中的每一个元素:
// control/ForInFloat.java
import java.util.*;
public class ForInFloat {
public static void main(String[] args) {
Random rand = new Random(47);
float[] f = new float[10];
for(int i = 0; i < 10; i++)
f[i] = rand.nextFloat();
for(float x : f)
System.out.println(x);
}
}
/* 输出:
0.72711575
0.39982635
0.5309454
0.0534122
0.16020656
0.57799757
0.18847865
0.4170137
0.51660204
0.73734957
*/
这个数组是用旧式的 for 循环来填充的,因为在填充时必须按索引访问。下面这行代码中使用的就是 for-in 语法:
这个数组是用旧式的 for 循环来填充的,因为在填充时必须按索引访问。下面这行代码中使用的就是 for-in 语法:for(float x : f) {
这条语句定义了一个 float 类型的变量 x,然后会将数组 f 里的每一个元素按顺序赋给 x。
任何返回了数组的方法都可以使用 for-in。例如,String 类有一个 toCharArray() 方法,它返回了一个 char 数组,因此你可以很容易地迭代字符串里的所有字符:
// control/ForInString.java
public class ForInString {
public static void main(String[] args) {
for(char c : "An African Swallow".toCharArray())
System.out.print(c + " ");
}
}
/* 输出:
A n A f r i c a n S w a l l o w
*/
在第 12 章中你会看到,for-in 还可以用于任何 Iterable 对象。
许多for语句会在一个整数值序列中步进,就像下面这样:for(int i = 0; i < 100; i++)
对于这些语句,for-in 语法不起作用,因为你需要先创建一个 int 数组。为了简化这些任务,我在onjava.Range
包里创建了一个 range() 方法,它会自动生成合适的数组。
第 7 章会介绍静态引入(static import),不过现在你就可以直接使用这个库,而不需要了解具体的细节。你可以在 import 这一行看到 static import 语法:
// control/ForInInt.java
import static onjava.Range.*;
public class ForInInt {
public static void main(String[] args) {
for(int i : range(10)) // 0~9
System.out.print(i + " ");
System.out.println();
for(int i : range(5, 10)) // 5~9
System.out.print(i + " ");
System.out.println();
for(int i : range(5, 20, 3)) // 5~20,步进3
System.out.print(i + " ");
System.out.println();
for(int i : range(20, 5, -3)) // 倒计时
System.out.print(i + " ");
System.out.println();
}
}
/* 输出:
0 1 2 3 4 5 6 7 8 9
5 6 7 8 9
5 8 11 14 17
20 17 14 11 8
*/
range() 方法已经被重载(overloaded),重载表示方法名相同但具有不同的参数列表(你很快就会学到重载)。range() 方法的第一种重载形式是从 0 开始产生值,直到范围的上限,但不包括这个上限。第二种形式是从第一个参数值开始产生值,直到比第二个参数值小为止。第三种形式有一个步进值,它每次增加的步幅为该值。在第四种形式中可以倒计时。range() 其实就是一个简化版的生成器,本书稍后会介绍它。
range() 方法让我们可以在更多的地方使用 for-in 语法,因此可以说提高了可读性。
注意 System.out.print() 不会生成换行,你可以在一行里输出相关内容。
for-in 语法不仅方便代码的编写,更重要的是让代码更容易阅读,它表明了你打算做什么(获取数组中的每一个元素),而不是给出如何做的实现细节(“我创建了一个索引,用来选取数组里的每个元素”)。本书更提倡使用 for-in 语法。
// onjava/Range.java
package onjava;
public class Range {
// Produce sequence [start..end) incrementing by step
public static int[] range(int start, int end, int step) {
if(step == 0)
throw new
IllegalArgumentException("Step cannot be zero");
int sz = Math.max(0, step >= 0 ?
(end + step - 1 - start) / step
: (end + step + 1 - start) / step);
int[] result = new int[sz];
for(int i = 0; i < sz; i++)
result[i] = start + (i * step);
return result;
} // Produce a sequence [start..end)
public static int[] range(int start, int end) {
return range(start, end, 1);
}
// Produce a sequence [0..n)
public static int[] range(int n) {
return range(0, n);
}
}