迭代對於我們搞Java的來說絕對不陌生。我們常常使用JDK提供的迭代介面進行Java集合的迭代。
Iterator iterator = list.iterator(); while(iterator.hasNext()){ String string = iterator.next(); //do something }
迭代其實裡面的典型設計為歷,是一個標準化的各項容器的典型方法。 Iterator模式是用於遍歷集合類別的標準存取方法。它可以把存取邏輯從不同類型的集合類別中抽象化出來,從而避免向客戶端揭露集合的內部結構。 在沒有迭代器時我們都是這樣處理的。如下:
對於陣列我們是使用下標來處理的:
對於ArrayList是這麼處理的:
int[] arrays = new int[10];
for(int i = 0 ; i < arrays.length ; i++){
int a = arrays[i];
//do something
}
對於這兩種方式,我們總是事先都知道集合的內部結構,存取程式碼和集合本身是緊密耦合的,無法將存取邏輯從集合類別和客戶端程式碼中分離出來。同時每一種集合對應一種遍歷方法,客戶端程式碼無法重複使用。 在實際應用中如何需要將上面將兩個集合整合是相當麻煩的。所以為了解決以上問題,Iterator模式騰空出世,它總是用同一種邏輯遍歷集合。使得客戶端本身不需要來維護集合的內部結構,所有的內部狀態都由Iterator來維護。客戶端從不直接和集合類別打交道,它總是控制Iterator,向它發送"向前","向後","取當前元素"的命令,就可以間接遍歷整個集合。
上面只是對Iterator模式進行簡單的說明,下面我們看看Java中Iterator接口,看看他是如何來進行實現的。
一、java.util.Iterator在Java中將迭代器為一個介面,它只提供了迭代了基本規則,在JDK 中對他進行迭代的迭代規則是這樣定義了。迭代器取代了 Java Collections Framework 中的 Enumeration。迭代器與枚舉有兩點不同:
1、迭代器允許呼叫者利用定義良好的語意在迭代期間從迭代器所指向的 collection 移除元素。
2、方法名稱改善了。
其介面定義如下:
List<String> list = new ArrayList<String>();
for(int i = 0 ; i < list.size() ; i++){
String string = list.get(i);
//do something
}
Object next():返回迭代器剛越過的元素的引用,回傳值是Object,需要強制轉換成自己需要的類型
元素
對於我們而言,我們只需使用next()、hasNext()兩個方法即可完成迭代。如下:
public interface Iterator {
boolean hasNext();
Object next();
void remove();
}
前面阐述了Iterator有一个很大的优点,就是我们不必知道集合的内部结果,集合的内部结构、状态由Iterator来维持,通过统一的方法hasNext()、next()来判断、获取下一个元素,至于具体的内部实现我们就不用关心了。但是作为一个合格的程序员我们非常有必要来弄清楚Iterator的实现。下面就ArrayList的源码进行分析分析。
下面就ArrayList的Iterator实现来分析,其实如果我们理解了ArrayList、Hashset、TreeSet的数据结构,内部实现,对于他们是如何实现Iterator也会胸有成竹的。因为ArrayList的内部实现采用数组,所以我们只需要记录相应位置的索引即可,其方法的实现比较简单。
在ArrayList内部首先是定义一个内部类Itr,该内部类实现Iterator接口,如下:
private class Itr implements Iterator<E> { //do something }
而ArrayList的iterator()方法实现:
public Iterator<E> iterator() { return new Itr(); }
所以通过使用ArrayList.iterator()方法返回的是Itr()内部类,所以现在我们需要关心的就是Itr()内部类的实现:
在Itr内部定义了三个int型的变量:cursor、lastRet、expectedModCount。其中cursor表示下一个元素的索引位置,lastRet表示上一个元素的索引位置
int cursor; int lastRet = -1; int expectedModCount = modCount;
从cursor、lastRet定义可以看出,lastRet一直比cursor少一所以hasNext()实现方法异常简单,只需要判断cursor和lastRet是否相等即可。
public boolean hasNext() { return cursor != size; }
对于next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可,
public E next() { checkForComodification(); int i = cursor; //记录索引位置 if (i >= size) //如果获取元素大于集合元素个数,则抛出异常 throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; //cursor + 1 return (E) elementData[lastRet = i]; //lastRet + 1 且返回cursor处元素 }
checkForComodification()主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过。在java提高篇(二一)-----ArrayList中已经阐述了。modCount用于记录ArrayList集合的修改次数,初始化为0,,每当集合被修改一次(结构上面的修改,内部update不算),如add、remove等方法,modCount + 1,所以如果modCount不变,则表示集合内容没有被修改。该机制主要是用于实现ArrayList集合的快速失败机制,在Java的集合中,较大一部分集合是存在快速失败机制的,这里就不多说,后面会讲到。所以要保证在遍历过程中不出错误,我们就应该保证在遍历过程中不会对集合产生结构上的修改(当然remove方法除外),出现了异常错误,我们就应该认真检查程序是否出错而不是catch后不做处理。
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
对于remove()方法的是实现,它是调用ArrayList本身的remove()方法删除lastRet位置元素,然后修改modCount即可。
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
这里就对ArrayList的Iterator实现讲解到这里,对于Hashset、TreeSet等集合的Iterator实现,各位如果感兴趣可以继续研究,个人认为在研究这些集合的源码之前,有必要对该集合的数据结构有清晰的认识,这样会达到事半功倍的效果!!!!
—————————————————————————————————————————————— ———————————
以上就是java提高篇(三十)-----Iterator的內容,更多相關內容請關注PHP中文網(www.php .cn)!