Java迭代器移除元素注意的問題
Java迭代器一共有3種,最常用的一種是Iterator
Iterator
目前最常用的一種迭代器,在這中相當於有一個指標,指向第一個數的前面
兩種主要用的方法:
1.hadNext()下一個物件是否還存在
2.next() 取出下一個數,指標指向下一位。
Iterator it=list.iterator();
for (Iterator it=list.iterator();it.hasNext();) {
System.out.println(it.next());
}
ListIterator
雙向迭代器
Enumeration
相當於Iterator,現在已被取代。
解決使用迭代器刪除元素拋異常問題
- 遍歷物件的方法
Iterator it=list.iterator();
//方式一:使用while迴圈
while(it.hasNext()){
System.out.println(it.next());
}
//方式二:使用foreach,簡單,不用自己建立迭代器物件,在底層仍然是迭代器的for迴圈。
for (Object e : list) {
System.out.println(e);
}
/*
for(Iteratorit=list.iterator();it.hasNext();e=i t.next()) {
System.out.println(e);
}
*/
- 1
由於foreach迴圈比較簡單,所以一般我們在遍歷陣列和集合的時候,都會優先考慮使用它。
但是在需要邊迭代邊刪除的情況下,不能使用foreach,必須顯示寫出迭代器物件,否則會報錯: java.util.ConcurrentModificationException
在API中對此異常的解釋如下:
public class ConcurrentModificationException extends RuntimeException
此異常可能會被丟擲的方法,已檢測到的物件的併發修改時,這樣的修改是不允許的。
例如,它通常是不允許一個執行緒而另一個執行緒遍歷它修改集合。在一般情況下,迭代的結果是不確定的,在這種情況下。一些迭代器實現(包括所有通用收集實現的JRE提供)可以選擇如果檢測行為丟擲該異常。迭代器這樣做被稱為快速失敗迭代器,因為他們不能迅速、乾淨,而冒著任意的,非在將來一個不確定的時間確定的行為。注意,這個例外並不總是表明物件已由一個不同的執行緒的併發性。如果一個執行緒問題序列的方法呼叫,違反合同的物件,物件可能丟擲該異常。例如,如果一個執行緒修改直接收集的則是在一個快速失敗迭代器集合的迭代,迭代器將丟擲此異常。
注意,快速失敗行為不能得到保證的話,一般來說,不可能在不同步的併發修改的存在作出難以保證。快速失敗的操作把ConcurrentModificationException盡最大努力的基礎上。因此,要寫一個程式,依靠這一例外的正確性錯誤:concurrentmodificationexception只能用來檢測錯誤。
- 以上情況分析:
在使用Iterator的時候,迭代器會新建一個執行緒,把原來的執行緒中的物件重新拷貝一份,在進行刪除,修改等操作時,原來的執行緒只負責迭代,而Iterator負責迭代和刪除操作,Iterator每次迭代都會檢查迭代器裡的物件和原執行緒中的物件個數是否一致,不一致則丟擲:ConcurrentModificationException。
for (Object e : list) {
System.out.println(e);
if("b".equals(e)){
list.remove(e);//操作集合的刪除方法
}
}
執行結果:
a
b
Exception in thread "main" java.util.ConcurrentModificationException
- 以上情況解決辦法
不能使用集合中的remove方法,使用Iterrator中的remove方法。
Iterator中的remove
default void remove()
從基礎集合中移除這個迭代器返回的最後一個元素(可選操作)。兩個執行緒中都刪除,保證執行緒的同步。
修改後的程式碼:
Iterator it=list.iterator();
while(it.hasNext()){
Object e=it.next();
if("b".equals(e)){
it.remove();
}
}
System.out.println(list);
總結
-
在需要的刪除等操作時,不能使用簡單的foreach,因為其底層依然用的是Iterator,但是呼叫的是集合中的remove方法。
-
使用迭代器物件呼叫其中的remove方法,以保證執行緒同步。