- 所有的集合類(List、Set...)都實作自Collection接口,而Collection接口又繼承于Iterable接口,是以可以說所有的集合類(List、Set...)都實作了Iterable接口
- 當某個類實作Iterable接口時,我們就能稱這個類是一個 "可數" 的類,也就是可以使用
擷取一個疊代器Iterator,然後使用這個Iterator執行個體去周遊這個類,是以所有的Collection類都能夠使用疊代器Iterator來周遊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時,疊代器就越過下一個元素,并傳回剛剛越過的那個元素的引用
- 如果某個類實作了Iterable接口,那麽他也需要建立一個内部類去實作一個Iterator類,讓調用Iterable接口中的iterator()時,能夠擷取到一個iterator執行個體
- 使用疊代器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; } } } }
- Iterable接口
- 而再進一步說,當某個類能使用疊代器Iterator來周遊時,就能使用java提供的foreach文法糖來周遊此類 (foreach文法糖其實就是簡化的
)iterator()
- foreach實際上會被編譯器編譯成使用疊代器
去周遊集合,是以能使用foreach的,都是得實作Iterable接口的集合類Collection們,像是List、Setiterator()
- 是以Map就沒有辦法直接使用foreach(因為Map沒有實作Iterable接口),隻有他的
、map.entrySet()
、map.keySet()
這種傳回一個集合類的方法,才能使用foreachmap.values()
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); } } }
- foreach實際上會被編譯器編譯成使用疊代器
- 為什麽Iterator要額外使用内部類去實作,而不是ArrayList直接實作此接口 ?
- 如果看過Collection類的源碼(以ArrayList為例),可以發現ArrayList類并不是由ArrayList去實作Iterator接口,而是ArrayList有一個内部類 Itr,專門去實作Iterator接口,而ArrayList的
方法,隻是去建立一個内部類ArrayList.Itr的執行個體而已iterator()
//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接口
- 如此當需要用到Iterator來周遊集合時,隻需要調用
,就能取得一個全新的、不受别人影響的疊代器供自己使用,而疊代器彼此之間也不會互相幹涉list.iterator()
- 至于為什麽要特别使用内部類來實作Iterator接口,而不是建立一個Iterator公共類來供所有集合一起使用,是因為疊代器需要知道集合的内部結構,他才能知道要怎麽去實作
、hasNext()
、next()
方法,而使用内部類才能無條件的取用外部類的所有資訊(包含private的變量和方法),是以才需要将Iterator提取成接口,讓每個集合自己使用内部類去實作Iterator接口remove()
- 如此當需要用到Iterator來周遊集合時,隻需要調用
- 如果看過Collection類的源碼(以ArrayList為例),可以發現ArrayList類并不是由ArrayList去實作Iterator接口,而是ArrayList有一個内部類 Itr,專門去實作Iterator接口,而ArrayList的