所有的集合類(List、Set…)都實現自 Collection 接口,而 Collection 接口又繼承於 Iterable 接口,因此可以說所有的集合類(List、Set…)都實現了 Iterable 接口
當某個類實現 Iterable 接口時,我們就能稱這個類是一個 “可數” 的類,也就是可以使用 iterator() 獲取一個迭代器 Iterator,然後使用這個 Iterator 實例去遍歷這個類,因此所有的 Collection 類都能夠使用迭代器 Iterator 來遍歷
Iterable 接口
public interface Iterable<T> {
//當某個類實現Iterable接口的話,就能獲取到迭代器iterator,然後就能使用這個iterator去遍歷此類
Iterator<T> iterator();
}
Iterator 接口
如果某個類實現了 Iterable 接口,那麼他也需要創建一個內部類去實現一個 Iterator 類,讓調用 Iterable 接口中的 iterator() 時,能夠獲取到一個 iterator 實例
public interface Iterator<E> {
//是否有下一個元素
boolean hasNext();
//取得下一個元素
E next();
//刪除最後一個獲取的元素,因此調用remove()前一定得先調用一次next()
void remove();
}
至於此 Iterator 接口怎麼實現,就看各個集合實現類如何定義 “下一個元素”,像是 ArrayList 的下一個元素就是 element[index+1],而 HashMap 的下一個元素則是 hash table 數組中儲存的下一個 entry
另外可以想像 Iterator 像是一個游標一樣,一開始停在最前面,然後不停的往後走(只能向後移動),且此游標每次都是停在元素和元素的中間,當調用 next 時,迭代器就越過下一個元素,並返回剛剛越過的那個元素的引用

使用迭代器 Iterator 遍歷 ArrayList
public class Main {
public static void main(String[] args) {
//ArrayList實現了Collection接口,因此他也實現了Iterable接口,所以他可以使用iterator迭代器來遍歷
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
//調用Iterable接口中的iterator()取得此ArrayList的迭代器實例
Iterator<String> its = list.iterator();
//使用Iterator接口的hasNext()、next()來遍歷此ArrayList集合實現類
while (true) {
if (its.hasNext()) {
String s = its.next();
System.out.println(s);
} else {
break;
}
}
}
}
而再進一步說,當某個類能使用迭代器 Iterator 來遍歷時,就能使用 java 提供的 foreach 語法糖來遍歷此類(foreach語法糖其實就是簡化的 iterator())
foreach 實際上會被編譯器編譯成使用迭代器 iterator() 去遍歷集合,因此能使用 foreach 的,都是得實現 Iterable 接口的集合類 Collection 們,像是 List、Set
所以 Map 就沒有辦法直接使用 foreach(因為 Map 沒有實現 Iterable 接口),只有他的 map.entrySet()、map.keySet()、map.values() 這種返回一個集合類的方法,才能使用 foreach
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
//原代碼,使用語法糖的foreach
for (String s : list) {
System.out.println(s);
}
//實際上會被編譯成使用iterator去遍歷
for (Iterator<String> its = list.iterator(); its.hasNext(); ) {
String s = its.next();
System.out.println(s);
}
}
}
為什麼 Iterator 要額外使用內部類去實現,而不是 ArrayList 直接實現此接口 ?
如果看過 Collection 類的源碼(以ArrayList為例),可以發現 ArrayList 類並不是由 ArrayList 去實現 Iterator 接口,而是 ArrayList 有一個內部類 Itr,專門去實現 Iterator 接口,而 ArrayList 的 iterator() 方法,只是去創建一個內部類 ArrayList.Itr 的實例而已
//ArrayList不實現Iterator接口,反而是由他的內部類進行實現
public class ArrayList<E> extends AbstractList<E> {
//調用list.iterator()可以取得此list的迭代器
public Iterator<E> iterator() {
return new Itr(); //實際上就是去創建一個內部類的實例
}
//ArrayList中的內部類Itr,專門實現Iterator接口
private class Itr implements Iterator<E> {
int cursor; //記錄當前迭代到哪裡
public boolean hasNext() { ... }
public E next() { ... }
public void remove() { ... }
}
}
要這樣設計是因為一個集合類可能同時有多個迭代器去遍歷他,而每個迭代器遍歷到集合的哪裡,是每個迭代器自己的事情,彼此不互相干涉,因此才需要額外使用一個內部類去實現迭代器的 Iterator 接口
list.iterator(),就能取得一個全新的、不受別人影響的迭代器供自己使用,而迭代器彼此之間也不會互相干涉hasNext()、next()、remove() 方法,而使用內部類才能無條件的取用外部類的所有信息(包含 private 的變量和方法),因此才需要將 Iterator 提取成接口,讓每個集合自己使用內部類去實現 Iterator 接口為什麼 Iterator 接口,只有 hasNext()、next()、remove() 方法,而沒有 add(E) 方法 ?
iterator.add() 方法,理論上來說,應該是要在當前這個元素 E1 後面新增一個元素 E2,使得下次遍歷此集合時,E2 一定會出現在 E1 後面,也就是 [….E1, E2, ….]add() 方法是以這個語意為前提的話,那麼迭代器不提供此方法是很合理的,對於有序的集合(像是ArrayList)來說,在此元素後面新增一個元素是一個很簡單的事情,但是對於無序的集合(像是HashSet)來說,不能保證新插入的這個元素 E2 一定會在 E1 後面(因為還得計算 HashCode),如此就違反了 add() 的語意了,這也就是為什麼 Iterator 接口不提供 add() 方法add() 方法,因此 Iterator 接口沒必要提供這個方法