1. 程式人生 > >面試題:Iterator遍歷的新增刪除

面試題:Iterator遍歷的新增刪除

list是一個ArrayList的物件,哪個選項的程式碼填到//todo delete處,可以在Iterator遍歷的過程中正確並安全的刪除一個list中儲存的物件?()

Iterator it = list.iterator();
int index = 0;
while (it.hasNext())
{
    Object obj = it.next();
    if (needDelete(obj))  //needDelete返回boolean,決定是否要刪除
    {
        //todo delete
    }
    index ++;
}
it.remove();            √
list.remove
(obj); × list.remove(index); × list.remove(obj,index); ×

如果在迴圈的過程中呼叫集合的remove()方法,就會導致迴圈出錯,例如:

for(int i=0;i<list.size();i++){
    list.remove(...);
}

迴圈過程中list.size()的大小變化了,就導致了錯誤。
所以,如果你想在迴圈語句中刪除集合中的某個元素,就要用迭代器iterator的remove()方法,因為它的remove()方法不僅會刪除元素,還會維護一個標誌,用來記錄目前是不是可刪除狀態,例如,你不能連續兩次呼叫它的remove()方法,呼叫之前至少有一次next()方法的呼叫。

原始碼是這麼描述的:ArrayList 繼承了 AbstractList, 其中AbstractList 中有個modCount 代表了集合修改的次數。在ArrayList的iterator方法中會判斷 expectedModCount與 modCount是否相等,如果相等繼續執行,不相等報錯,只有iterator的remove方法會在呼叫自身的remove之後讓 expectedModCount與modCount再相等,所以是安全的。

在使用set/map時,一個可愛的小bug:java.util.ConcurrentModificationException

【錯誤場景1】:set容器,邊遍歷,邊add/remove元素

Set<String> set = new HashSet<String>();
for (int i = 0; i < 10000; i++) {
    set.add(Integer.toString(i));
}
for (String str : set) { //或使用iterator來迴圈,JDK5.0以上,這樣的遍歷底層也都是iterator實現。
    set.add("xxx"); //報錯
    //set.remove(str); //報錯

}
【錯誤場景2】:map容器,邊遍歷,邊remove元素

Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < 100; i++) {
    map.put(Integer.toString(i), Integer.toString(i));
}
for (String str : map.keySet()) {//或使用iterator來迴圈
    map.remove(str); //報錯
}

【錯誤場景3】list容器,邊遍歷,邊add/remove元素

List<String> list = new ArrayList<String>();
        for (int i = 0; i < 100; i++) {
            list.add(Integer.toString(i));
        }
        for (Iterator<String> it = list.iterator(); it.hasNext();) { 
            String val = it.next();
            if (val.equals("5")) {
                list.add(val); //報錯
                //list.remove(val);   //報錯    
            }
        }

【錯誤原因】

  • 對於remove操作,list.remove(o)的時候,只將modCount++,而expectedModCount值未變,那麼迭代器在取下一個元素的時候,發現該二值不等,則拋ConcurrentModificationException異常。
  • 對於add操作,同remove
  • 【解決辦法】

  • remove:用iterator提供的原生態remove()
  • add:同remove就錯了,iterator沒有提供原生的add()方法。要用新的容器暫存,然後再遍歷結束後,全部新增到原容器當中。
  • set/list:這兩類常用容器,就用上面說的方法remove(), add()就好了。
  • map:直接使用ConcurrentHashMap就ok。為什麼別的容器,不也實現個concurrent版本直接用。。?庫裡不搞,自己搞。
    【正確使用案例】
  • for (Iterator<String> it = list.iterator(); it.hasNext();) {
        String val = it.next();
        if (val.equals("5")) {
            it.remove(); 
        }
    }
    
    
    List<String> newList = new ArrayList<String>(); 
    for (Iterator<String> it = list.iterator(); it.hasNext();) {
        String val = it.next();
        if (val.equals("5")) {
            newList.add(val);
        }
    }
    list.addAll(newList);