如果要你寫個forEach()方法,可以顯示List收集的所有物件,也許你會這麼寫:
...
public static void forEach(List list) {
int size = list.size();
for(int i = 0; i < size; i++) {
out.println(list.get(i));
}
}
...
這個方法適用於所有實作List介面的物件,例如ArrayList、LinkedList等。如果要讓你寫個forEach()方法顯示Set收集的所有物件,你該怎麼寫呢?在查看過Set的API文件後,你發現有個toArray()方法,可以將Set收集的物件轉為Object[]傳回,所以你會這麼撰寫:
...
public static void forEach(Set set) {
for(Object o : set.toArray()) {
out.println(o);
}
}
...
這個方法適用於所有實作Set介面的物件,例如HashSet、TreeSet等。如果現在要讓你再實作一個forEach()方法,可以顯示Queue收集的所有物件,也許你會這麼寫:
...
public static void forEach(Queue queue) {
while(queue.peek() != null) {
out.println(queue.poll());
}
}
...
表面上看來好像正確,不過Queue的poll()方法會取出物件,當你顯示完Queue中所有物件,Queue也空了,這並不是你想要的結果,怎麼辦呢?
事實上,無論是List、Set或Queue,都會有個iterator()方法,這個方法在JDK1.4之前,是定義在Collection介面中,而List、Set、Queue繼承自Collection,所以也都擁有iterator()的行為。
iterator()方法會傳回java.util.Iterator介面的實作物件,這個物件包括了Collection收集的所有物件,你可以使用Iterator的hasNext()看看有無下一個物件,若有的話,再使用next()取得下一個物件。因此,無論是List、Set、Queue或任何Collection,都可以使用以下的forEach()來顯示所收集之物件:
...
public static void forEach(Collection collection) {
Iterator iterator = collection.iterator();
while(iterator.hasNext()) {
out.println(iterator.next());
}
}
...
在JDK5之後,原先定義在Collection中的iterator()方法,提昇至新的java.util.Iterable父介面,因此在JDK5之後,你可以使用以下的forEach()方法顯示收集的所有物件:
...
public static void forEach(Iterable iterable) {
Iterator iterator = iterable.iterator();
while(iterator.hasNext()) {
out.println(iterator.next());
}
}
...
任何實作Iterable的物件,都可以使用這個forEach()方法,而不一定要是Collection,像是 神奇的 foreach 中實作的IterableString。
在JDK5之後有了增強式for迴圈,先前看到它運用在陣列上,實際上,增強式for迴圈還可運用在實作Iterable介面的物件上,因此先前的forEach()方法,可以用增強式for迴圈更加簡化:
package cc.openhome;
import java.util.*;
public class ForEach {
public static void main(String[] args) {
List names = Arrays.asList("Justin", "Monica", "Irene");
forEach(names);
forEach(new HashSet(names));
forEach(new ArrayDeque(names));
}
public static void forEach(Iterable iterable) {
for(Object o : iterable) {
System.out.println(o);
}
}
}
這邊使用了java.util.Arrays的static方法asList(),這個方法接受不定長度引數,可將指定的引數收集為List。List是一種Iterable,所以可以使用forEach()方法。HashSet具有接受Collection的建構式,List是一種Collection,所以可用來建構HashSet,而Set是一種Iterable,所以可使用forEach()方法。同理,ArrayDeque具有接受Collection的建構式,List是一種Collection,所以可用來建構ArrayDeque,Deque是一種Iterable,所以可使用forEach()方法。
實際上增強式for迴圈是編譯器蜜糖,當運用在Iterable物件時,會展開為:
public static void forEach(Iterable iterable) {
Object o;
for(Iterator i\$ = iterable.iterator(); i\$.hasNext(); System.out.println(o)) {
o = i\$.next();
}
}
可以看到,實際上還是呼叫了iterator()方法,運用傳回的Iterator物件來迭代取得收集之物件。
如果使用JDK8,想要迭代物件還有新的選擇,Iterable上新增了forEach()方法,可以讓你迭代物件進行指定處理:
List<String> names = Arrays.asList("Justin", "Monica", "Irene");
names.forEach(name -> out.println(name));
new HashSet(names).forEach(name -> out.println(name));
new ArrayDeque(names).forEach(name -> out.println(name));
out的println()方法:
List<String> names = Arrays.asList("Justin", "Monica", "Irene");names.forEach(out::println);new HashSet(names).forEach(out::println);new ArrayDeque(names).forEach(out::println);嗯?
Iterable介面新增了forEach()方法?如果你瞭解JDK8之前的介面,應該知道,介面一但新增了方法,所有實作介面的類別都得實作該方法,現存各種API中實作Iterable介面的類別太多了,這樣不會造成這些類別因為沒有實作forEach(),而在JDK8上編譯錯誤嗎?不會的!因為JDK8演進了
interface語法,允許擁有預設方法(Default method),這在下一篇就會討論。

