Java基础篇:Iterator迭代器
一、什么是Iterator:
迭代器(Iterator)是一個對象,它的工作是遍歷并目標序列中的對象,它提供了一種訪問一個容器(container)對象中的各個元素的方法,把訪問邏輯從不同類型的集合類中抽象出來,又不必暴露該對象內部細節。通過迭代器,開發人員不需要了解容器底層的結構,就可以實現對容器的遍歷。由于創建迭代器的代價小,因此迭代器通常被稱為輕量級的容器。
常常使用JDK提供的迭代接口進行Java集合的迭代。
Iterator iterator = list.iterator();while(iterator.hasNext()){String string = iterator.next();//do something} 在沒有迭代器時,我們都是這么進行遍歷元素的,如下:對于數組,我們是使用下標進行處理的P:
int[] arrays = new int[10]; for(int i = 0 ; i < arrays.length ; i++){int a = arrays[i];//do something}對于ArrayList是這么處理的:
List<String> list = new ArrayList<String>();for(int i = 0 ; i < list.size() ; i++){String string = list.get(i);//do something}對于這兩種方式,我們總是都事先知道集合的內部結構,訪問代碼和集合本身是緊密耦合的,無法將訪問邏輯從集合類和客戶端代碼中分離出來。同時每一種集合對應一種遍歷方法,客戶端代碼無法復用。 在實際應用中如何需要將上面將兩個集合進行整合是相當麻煩的。所以為了解決以上問題,Iterator模式騰空出世,它總是用同一種邏輯來遍歷集合。使得客戶端自身不需要來維護集合的內部結構,所有的內部狀態都由Iterator來維護。客戶端從不直接和集合類打交道,它總是控制Iterator,向它發送"向前","向后","取當前元素"的命令,就可以間接遍歷整個集合。
?
二、java.util.Iterator
在Java中Iterator為一個接口,它只提供了迭代了基本規則,在JDK中他是這樣定義的:對 collection 進行迭代的迭代器。迭代器取代了 Java Collections Framework 中的 Enumeration。迭代器與枚舉有兩點不同:
????????1、迭代器允許調用者利用定義良好的語義在迭代期間從迭代器所指向的 collection 移除元素。
????????2、方法名稱得到了改進。
????????其接口定義如下:
public interface Iterator {boolean hasNext();Object next();void remove(); }Object next():返回迭代器剛越過的元素的引用,返回值是Object,需要強制轉換成自己需要的類型;
boolean hasNext():判斷容器內是否還有可供訪問的元素;
void remove():刪除迭代器剛越過的元素;
?
三、各個集合的Iterator的實現:
1、ArrayList的Iterator實現:
在ArrayList內部首先是定義一個內部類Itr,該內部類實現Iterator接口,如下:
private class Itr implements Iterator<E> {int cursor; // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;//do something }而ArrayList的iterator()方法實現:
public Iterator<E> iterator() {return new Itr();}所以通過使用ArrayList.iterator()方法返回的是Itr()內部類,所以現在我們需要關心的就是Itr()內部類的實現:
在Itr內部定義了三個int型的變量:cursor、lastRet、expectedModCount。其中cursor表示下一個元素的索引位置,lastRet表示上一個元素的索引位置。
從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 + 1return (E) elementData[lastRet = i]; //lastRet + 1 且返回cursor處元素} final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}checkForComodification()主要用來判斷集合的修改次數是否合法,即用來判斷遍歷過程中集合是否被修改過。modCount用于記錄ArrayList集合的修改次數,初始化為0,每當集合被修改一次(結構上面的修改,內部update不算),如add、remove等方法,modCount + 1,所以如果modCount不變,則表示集合內容沒有被修改。該機制主要是用于實現ArrayList集合的快速失敗機制,在Java的集合中,較大一部分集合是存在快速失敗機制的。所以要保證在遍歷過程中不出錯誤,我們就應該保證在遍歷過程中不會對集合產生結構上的修改(當然remove方法除外),出現了異常錯誤,我們就應該認真檢查程序是否出錯而不是catch后不做處理。
對于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();}}2、ListIterator:
接口Iterator在不同的子接口中會根據情況進行功能的擴展,例如針對List的迭代器ListIterator,該迭代器只能用于各種List類的訪問。ListIterator可以雙向移動。添加了previous()等方法。如果是List集合,想要在迭代中操作元素可以使用List集合的特有迭代器ListIterator,該迭代器支持在迭代過程中,添加元素和修改元素。
?
四、for循環、forEach、Iterator對比:
相同點:都是用于遍歷集合元素的。
不同點:
1、形式差別:
//for循環的形式是:
for(int i=0;i<arr.size();i++){...}
//foreach的形式是:
for(int i:arr){...}
//iterator的形式是
Iterator it = arr.iterator();
while(it.hasNext()){ object o =it.next(); ...}
2、條件差別:
(1)for循環需要知道集合或數組的大小,而且需要是有序的,不然無法遍歷;
(2)foreach和iterator都不需要知道集合或數組的大小,他們都是得到集合內的每個元素然后進行處理;
3、多態差別:
(1)for和foreach都需要先知道集合的類型,甚至是集合內元素的類型,即需要訪問內部的成員,不能實現多態;
(2)iterator是一個接口類型,可以使用相同方式去遍歷不同集合中元素,而不用考慮集合類的內部實現(只要它實現了 java.lang.Iterable 接口),而且他還能隨時修改和刪除集合的元素,能夠將遍歷序列的操作與序列底層的結構分離。迭代器統一了對容器的訪問方式。這也是接口的解耦的最好體現。
例如:如果使用 Iterator 來遍歷集合中元素,一旦不再使用 List 轉而使用 Set 來組織數據,那遍歷元素的代碼不用做任何修改,如果使用 for 來遍歷,那所有遍歷此集合的算法都得做相應調整,因為List有序,Set無序,結構不同,他們的訪問算法也不一樣。
4、效率差別:
(1)采用ArrayList對隨機訪問比較快,而for循環中的get()方法,采用的即是隨機訪問的方法,因此在ArrayList里,for循環較快。
(2)采用LinkedList則是順序訪問比較快,iterator中的next()方法,采用的即是順序訪問的方法,因此在LinkedList里,使用iterator較快。
(3)從數據結構角度分析,for循環適合訪問順序結構,可以根據下標快速獲取指定元素.而Iterator 適合訪問鏈式結構,因為迭代器是通過next()和Pre()來定位的.可以訪問沒有順序的集合。
5、foreach 和 iterator 的其他區別:
使用foreach循環語句的優勢在于更加簡潔,更不容易出錯,不必關心下標的起始值和終止值,底層由iterator實現的,他們最大的不同之處就在于remove()方法上。
如果在forEach循環的過程中調用集合的remove()方法,就會導致循環出錯,因為循環過程中list.size()的大小變化了,就導致了錯誤。 所以,如果想在循環語句中刪除集合中的某個元素,就要用迭代器iterator的remove()方法,因為它的remove()方法不僅會刪除元素,還會維護一個標志,用來記錄目前是不是可刪除狀態,例如,你不能連續兩次調用它的remove()方法,調用之前至少有一次next()方法的調用。
?
參考文章:
https://blog.csdn.net/iamkila/article/details/7266890?utm_source=blogxgwz6
https://blog.csdn.net/chenssy/article/details/37521461
總結
以上是生活随笔為你收集整理的Java基础篇:Iterator迭代器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java集合篇:Vector
- 下一篇: Java集合篇:Stack