如果要你寫個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),這在下一篇就會討論。