java 迭代器
一 概念
迭代器是一個物件,它的工作是遍歷並選擇序列中的物件,它提供了一種訪問一個容器物件中的各個元素的方法,而不必暴露容器物件的內部細節。
作用:
1 通過迭代器,開發人員不需要了解容器結構,就可以遍歷容器元素。被稱為輕量級容器(建立迭代器代價很小)
2 它的特點是更加安全,因為它可以確保,在當前遍歷的集合元素被更改的時候,就會丟擲ConcurrentModificationException
異常
二 用法
Java中的Iterator功能比較簡單,並且只能單向移動:
(1) 使用方法iterator()要求容器返回一個Iterator物件。第一次呼叫Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable介面,被Collection繼承。
(2) 使用next()獲得序列中的下一個元素。
(3) 使用hasNext()檢查序列中是否還有元素。
(4) 使用remove()將迭代器新返回的元素刪除。
簡單例子
import java.util.*; public class Muster { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); Iterator itView Code= list.iterator(); while(it.hasNext()){ String str = (String) it.next(); System.out.println(str); } } }
三 迭代器是失效問題(刪除或新增元素)
在建立迭代器之後,除非通過迭代器自身的remove或add方法從結構上對列表進行修改,否則在任何時間以任何方式對列表進行修改,迭代器都會丟擲ConcurrentModificationException
。
public class ArrayListTest {public static void main(String args[]) { List<String> strList = new ArrayList<String>(); //迭代器 Iterator<String> iterator = strList.iterator(); //修改了集合 for (int i = 0; i < 10; i++) { strList.add("string" + i); } while (iterator.hasNext()) { System.out.println(iterator.next()); } }
執行該段程式碼,會發現其丟擲如下異常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.xyh.collection.ArrayListTest.main(ArrayListTest.java:21)
原因在於在迭代器建立之後,通過ArrayList自身的add方法對列表進行了修改,導致迭代器失效。當將藍色建立迭代器的程式碼移動到while迴圈的上方後,則不會出現該問題。即建立迭代器後不能再通過容器的add/remove方法來改變容器的資料,否則會導致迭代器的失效。
包括下面這種寫法,也是會導致同樣的異常
public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); list.add("e"); list.add("f"); list.add("g"); list.add("h"); Iterator<String> it = list.iterator(); while (it.hasNext()) { String str = it.next(); if (str.equals("f")) { list.remove(str); } } }View Code
上面異常的本質是
remove操作裡涉及到的expectedModCount=modCount; 值為呼叫容器的iterator()方法返回iterator物件時,容器中的元素個數
在網上查到說這是集合迭代中的一種“快速失敗”機制,這種機制提供迭代過程中集合的安全性。
從原始碼裡可以看到增刪操作都會使modCount++,通過和expectedModCount的對比,迭代器可以快速的知道迭代過程中是否存在list.add()類似的操作,存在的話快速失敗!而我們也知道,集合元素的刪除,不能用foreach 這又是為什麼?
foreach中的remove方法實際上使用list.remove一樣會報ConcurrentModificationException異常。因為foreach在jvm中還是會解析成Iterator來執行的,實際上和錯誤例子是一樣的效果。那麼,我們再來看下為什麼用迭代器刪除時就可以安全的刪除,不會報錯呢?
在他的remove函式中可以看到下面的一句話,首先其實還是呼叫了ArrayList的remove函式
ArrayList.this.remove(lastRet)
但是在呼叫完該函式後,他又進行了如下操作
expectedModCount = modCount;
相當於將最新的版本號告訴了迭代器,所以迭代器在進行異常檢查的時候就不會報錯,因為他倆是相等的
四 迭代器刪除元素
public class ArrayListTest { public static void main(String args[]) throws Exception { List<String> strList = new ArrayList<String>(); for (int i = 0; i < 10; i++) { strList.add("string" + i); } Iterator<String> iterator = strList.iterator(); while (iterator.hasNext()) { //iterator.next() 遊標指向了下一個元素 if (iterator.next().equals("string3")) { iterator.remove(); //iterator.remove()移除的是最近一次iterator.next()所獲取的物件 } } iterator = strList.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }
上述程式碼中,iterator.remove操作移除的物件時string3。如果將藍色while迴圈替換為如下的程式碼:
int index = 0; while (iterator.hasNext()) { if (++index == 3) { iterator.remove(); } System.out.println(iterator.next());
本程式碼的初衷是希望通過使用迭代器來刪除第三個元素即string2,並將未刪除的元素依次打印出來,殊不知刪除的卻是string1元素,即:iterator.remove()操作刪除的是上一次next元素獲取的物件。因此在這裡可以發現,如果要通過迭代器刪除一個元素,首先要通過next方法獲取該元素。
需要刪除元素,也可以把迭代器遍歷過程中,把需要刪除的元素放入新集合,遍歷完成後一次性刪除 removeAll()