如果你要走訪陣列中所有元素,通常你會這麼寫:
public void go(int[] arr) {
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
事實上很多時候,從頭到尾走訪一個陣列(或Collection)中所有元素,是很常見的需求,上面的寫法並沒有錯,只不過索引資訊基本上是不需要的,硬要寫是很冗長的事。
在J2SE 5.0之後,引進了foreach新語法,上例你可以這麼改寫:
public void go(int[] arr) {
for(int element : arr) {
System.out.println(element);
}
}
for(int element : arr) {
System.out.println(element);
}
}
這個語法其實不僅限於陣列,還可以用在List、Set等。例如若是List實作物件,可以這麼寫:
public void go(List<String> list) {
for(String element : list) {
System.out.println(element);
}
}
for(String element : list) {
System.out.println(element);
}
}
如果是Set也是可以的,例如:
public void go(Set<String> set) {
for(String element : set) {
System.out.println(element);
}
}
for(String element : set) {
System.out.println(element);
}
}
事實上,只要是Collection介面的實作物件,都可以用foreach語法:
public void go(Collection<String> collection) {
for(String element : collection) {
System.out.println(element);
}
}
for(String element : collection) {
System.out.println(element);
}
}
Java的foreach語法,其實是編譯器給的語法蜜糖。如果foreach要走訪的是陣列,事實上,編譯器會自動展開為以下的程式碼:
public void go(int ai[]) {
int ai1[] = ai;
int i = ai1.length;
for(int j = 0; j < i; j++) {
int k = ai1[j];
System.out.println(k);
}
}
int ai1[] = ai;
int i = ai1.length;
for(int j = 0; j < i; j++) {
int k = ai1[j];
System.out.println(k);
}
}
若foreach運用在Collection的實作物件上,其實編譯器會展開為:
public void go(Collection collection) {
String s;
for(Iterator iterator = collection.iterator();
iterator.hasNext(); System.out.println(s))
s = (String)iterator.next();
}
String s;
for(Iterator iterator = collection.iterator();
iterator.hasNext(); System.out.println(s))
s = (String)iterator.next();
}
無論是Collection、List或Set,展開後皆利用iterator()方法傳回Iterator物件,並利用Iterator來移動、傳回下一個物件,這是 Iterator 模式 的實現。
在J2SE 1.4之前,Collection介面上直接定義了iterator()方法必須實作,因此所有Collection實作物件,皆需實作iterator ()。在J2SE 5.0之後,引進了Iterable介面,在介面中定義了iterator()方法,該方法必須傳回Iterator物件,而Collection介面繼 承了Iterable介面,因此所有Collection實作物件,皆需實作iterator(),因此所有Collection實作物件,都可以搭配使用foreach語法。
然而並非只有Collection介面的實作物件,才可以搭配foreach語法,事實上,由於Iterable介面的引進,使得只要是實作Iterable介面的物件,皆可搭配foreach語法來使用。例如:
public void go(Iterable<String> iterable) {
for(String element : iterable) {
System.out.println(element);
}
}
for(String element : iterable) {
System.out.println(element);
}
}
不管你是List、Set還是Collection,只要是Iterable的物件,都可以用這個go,或是直接用foreach語法,像是:
- Some.java
import java.util.Iterator;
public class IterableString implements Iterable<Character> {
private String original;
public IterableString(String original) {
this.original = original;
}
public Iterator<Character> iterator() {
return new InnerIterator();
}
private class InnerIterator implements Iterator<Character> {
private int index;
public boolean hasNext() {
return index < original.length();
}
public Character next() {
Character c = original.charAt(index);
index++;
return c;
}
public void remove() {}
}
}
IterableString類別實作了Iterable介面,因此你可以像以下這麼搭配foreach:
- Main.java
public class Main {
public static void main(String[] args) {
IterableString str = new IterableString("Justin");
for(Character c : str) {
System.out.println(c);
}
}
}
若你反組譯程式,會發現編譯器作了這些事:
Character c;
for(Iterator iterator = str.iterator(); iterator.hasNext();
System.out.println(c))
c = (Character)iterator.next();
for(Iterator iterator = str.iterator(); iterator.hasNext();
System.out.println(c))
c = (Character)iterator.next();
在JDK7之後,有不少API都實作了Iterable,例如NIO2的Path物件等。