首先大家先看一段代碼:
public static void main(String[] args) {
List<String> listStr = new ArrayList<String>();
listStr.add("1");
listStr.add("2");
listStr.add("3");
listStr.add("4");
listStr.add("5");
System.out.println("listStr.size:::"+listStr.size());
for(String str:listStr){
if("3".equals(str)){
listStr.remove(str);
}
}
System.out.println("listStr.size:::"+listStr.size());
}
現象:
該程式會抛出一個 java.util.ConcurrentModificationException異常。
分析:
對 Collection 或 Map 進行疊代操作過程中嘗試直接修改 Collection / Map 的内容時,會抛出java.util.ConcurrentModificationException 異常。 因為在修改資料時不能同步原有list中資料,當下一次循環時找不到資料。
解決辦法:
Iterator 在工作的時候是不允許被疊代的對象被改變的,使用 Iterator 本身的方法 remove() 來删除對
象, Iterator.remove() 方法會在删除目前疊代對象的同時維護索引的一緻性。
改後代碼:
public static void main(String[] args) {
List<String> listStr = new ArrayList<String>();
listStr.add("1");
listStr.add("2");
listStr.add("3");
listStr.add("4");
listStr.add("5");
System.out.println("listStr.size:::"+listStr.size());
Iterator<String> ite = listStr.iterator();
while(ite.hasNext()){
String str = (String)ite.next();
if("3".equals(str)){
ite.remove();
}
}
System.out.println("listStr.size:::"+listStr.size());
}
補充:
或許有人可能會問如果我不用增強for循環,直接用.get(index)方法會不會報錯?
代碼如下:
for(int i = 0 ; i < listStr.size(); i++){
if("3".equals(listStr.get(i))){
listStr.remove(i);
}
}
現象:
listStr.size:::5
listStr.size:::4
大家可以看到通過list的下表索引來修改list資料是不會出錯的。
分析:
大家可以這樣認為:通過所引來循環時,jvm記錄的是所引值,當移除目前對象後,其他元素的索引号不會同步改變。下次循環仍可以找到對應資料。而增強for循環,記錄的是目前對象,當下次循環時,會先找到該對象,然後遊标向下移,這時候找不到對象是以結果會混亂。
java.util.ConcurrentModificationException
【引言】
經常在疊代集合元素時,會想對集合做修改(add/remove)操作,類似下面這段代碼:
[java] view plain copy
- for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
- Integer val = it.next();
- if (val == 5) {
- list.remove(val);
- }
- }
運作這段代碼,會抛出異常java.util.ConcurrentModificationException。
【解惑】
(以ArrayList來講解)在ArrayList中,它的修改操作(add/remove)都會對modCount這個字段+1,modCount可以看作一個版本号,每次集合中的元素被修改後,都會+1(即使溢出)。接下來再看看AbsrtactList中iteraor方法
[java] view plain copy
- public Iterator<E> iterator() {
- return new Itr();
- }
它傳回一個内部類,這個類實作了iterator接口,代碼如下:
[java] view plain copy
- private class Itr implements Iterator<E> {
- int cursor = 0;
- int lastRet = -1;
- int expectedModCount = modCount;
- public boolean hasNext() {
- return cursor != size();
- }
- public E next() {
- checkForComodification();
- try {
- E next = get(cursor);
- lastRet = cursor++;
- return next;
- } catch (IndexOutOfBoundsException e) {
- checkForComodification();
- throw new NoSuchElementException();
- }
- }
- public void remove() {
- if (lastRet == -1)
- throw new IllegalStateException();
- checkForComodification();
- try {
- AbstractList.this.remove(lastRet);
- if (lastRet < cursor)
- cursor--;
- lastRet = -1;
- // 修改expectedModCount 的值
- expectedModCount = modCount;
- } catch (IndexOutOfBoundsException e) {
- throw new ConcurrentModificationException();
- }
- }
- final void checkForComodification() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- }
- }
在内部類Itr中,有一個字段expectedModCount ,初始化時等于modCount,即當我們調用list.iterator()傳回疊代器時,該字段被初始化為等于modCount。在類Itr中next/remove方法都有調用checkForComodification()方法,在該方法中檢測modCount == expectedModCount,如果不相當則抛出異常ConcurrentModificationException。
前面說過,在集合的修改操作(add/remove)中,都對modCount進行了+1。
在看看剛開始提出的那段代碼,在疊代過程中,執行list.remove(val),使得modCount+1,當下一次循環時,執行 it.next(),checkForComodification方法發現modCount != expectedModCount,則抛出異常。
【解決辦法】
如果想要在疊代的過程中,執行删除元素操作怎麼辦?
再來看看内部類Itr的remove()方法,在删除元素後,有這麼一句expectedModCount = modCount,同步修改expectedModCount 的值。是以,如果需要在使用疊代器疊代時,删除元素,可以使用疊代器提供的remove方法。對于add操作,則在整個疊代器疊代過程中是不允許的。 其他集合(Map/Set)使用疊代器疊代也是一樣。
//獲獎顯示
public String prizeList(){
voteList = userWorksService.getAllRank();
UserWorks userWorks = new UserWorks();
Iterator<UserWorks> list = voteList.iterator();
Integer rank ;
while(list.hasNext()){
userWorks = list.next();
if(!("9".equals(userWorks.getAwardsRank()))){
rank = Integer.parseInt(userWorks.getAwardsRank())+2;
userWorks.setAwardsRank(rank +"");
allList.add(userWorks);
}
}
voteList.removeAll(allList);
return "prizeList";
}