Iterator 與 Iterable
September 29, 2022如果要寫個 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
也空了,這並不是你想要的結果,怎麼辦呢?
Iterator/Iterable
無論是 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
。
增強式 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 迴圈
增強式 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));
發現了嗎?寫了三個重複的 Lambda 表示式,可以直接參考 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);