foreach只能用于數(shù)組和實現(xiàn)了Iterable接口的類
//Iterable JDK源碼
//可以通過成員內(nèi)部類,方法內(nèi)部類,甚至匿名內(nèi)部類去實現(xiàn)Iterator
public interface Iterable<T>{ Iterator<T> iterator();}
包含3個方法: hasNext , next , remove。remove按需求實現(xiàn),一般它很少用到,以至于Eclipse接口方法自動補全時,都忽略了remove放方法。
//Iterator接口的JDK源碼,注釋為整理建議使用Iterator的正確姿勢
public interface Iterator<E> { boolean hasNext(); //每次next之前,先調(diào)用此方法探測是否迭代到終點 E next(); //返回當前迭代元素 ,同時,迭代游標后移 /*刪除最近一次已近迭代出出去的那個元素。 只有當next執(zhí)行完后,才能調(diào)用remove函數(shù)。 比如你要刪除第一個元素,不能直接調(diào)用 remove() 而要先next一下( ); 在沒有先調(diào)用next 就調(diào)用remove方法是會拋出異常的。 這個和MySQL中的ResultSet很類似 */ void remove()
{ throw new UnsupportedOperationException("remove"); }}
需要理解的地方
1、hasNext , next , remove 的調(diào)用順序
2、迭代出來的是原集合元素拷貝!
下面是手動迭代的例子,foreach的原理和它一樣。
public static void main(String[] args){ List<Integer> li = new ArrayList<>(); li.add(1); li.add(2); li.add(3);
//不使用foreach 而手動迭代 Iterator<Integer> iter = li.iterator(); //獲取ArrayList 的迭代器 while(iter.hasNext()) //①先探測能否繼續(xù)迭代 { System.out.println(iter.next()); //②后取出本次迭代出的元素 //invoke remove() //③最后如果需要,調(diào)用remove } }
AbstractList中實現(xiàn)的迭代器類,可以借鑒參考。
我們實現(xiàn)自己的迭代器的情況很少,畢竟JDK集合足夠強大。
源碼中有一些保護機制,為了便于理解我刪改了。
private class Itr implements Iterator<E>
{ /*
AbstractList 中實現(xiàn)的迭代器,刪除了一些細節(jié)。不影響理解
Itr為一個priavate成員內(nèi)部類
*/ int cursor = 0; //馬上等待被迭代元素的index //最近一次,已經(jīng)被迭代出的元素的index,如果這個元素迭代后,被刪除了,則lastRet重置為-1 int lastRet = -1; public boolean hasNext() { return cursor != size(); //當前游標值 等于 集合的size() 說明已經(jīng)不能再迭代了。 } public E next() { int i = cursor; E next = get(i); lastRet = i; //lastRet 保存的是最近一次已經(jīng)被迭代出去的元素索引 cursor = i + 1; //cursor為馬上等待被迭代的元素的索引 return next; } public void remove() { if (lastRet < 0) //調(diào)用remove之前沒有調(diào)用next throw new IllegalStateException(); //則拋異常。這就是為什么在使用remove前,要next的原因 OuterList.this.remove(lastRet); //從集合中刪除這個元素 if (lastRet < cursor) //集合刪除元素后,集合后面的元素的索引會都減小1,cursor也要同步后移 cursor--; lastRet = -1; //重置 }}
迭代出來的元素都是原來集合元素的拷貝
Java集合中保存的元素實質(zhì)是對象的引用(可以理解為C中的指針),而非對象本身。
迭代出的元素也就都是 引用的拷貝,結(jié)果還是引用。那么,如果集合中保存的元素是可變類型的,我們就可以通過迭代出的元素修改原集合中的對象。
而對于不可變類型,如String 基本元素的包裝類型Integer 都是則不會反應(yīng)到原集合中。
為了便于理解,畫張圖:
驗證代碼:
public class Main{ public static void main(String[] args) { List<Person> li = new ArrayList<>(); Person p = new Person("Tom"); li.add(p); for(Person ap: li) { ap.setName("Jerry"); } System.out.println(li.get(0).getName()); //Jerry not Tom }}class Person{ public Person(String name) { this.name = (name==null?"":name); } private String name; public String getName() { return name; } public void setName(String name) { if(name == null) name = ""; this.name = name; } }
小試牛刀,讓自己的類支持迭代。
public class Main{ public static void main(String[] args) { MyString s = new MyString("1234567"); for(char c:s) { System.out.println(c); } }}class MyString implements Iterable<Character>{ private int length = 0; private String ineers = null; public MyString(String s) { this.ineers = s; this.length = s.length(); } @Override public Iterator<Character> iterator() { class iter implements Iterator<Character> //方法內(nèi)部類 { private int cur= 0; @Override public boolean hasNext() { return cur != length; } @Override public Character next() { Character c = ineers.charAt(cur); cur++; return c; } public void remove() { // do nothing } } return new iter(); //安裝Iterable接口的約定,返回迭代器 } }