概述
ConcurrentModificationException 可以直接從字面理解:同時(shí)更改異常渡讼。也就是說城菊,在對(duì)某一數(shù)據(jù)執(zhí)行某一操作的時(shí)候,同時(shí)更改了數(shù)據(jù)讹躯,造成錯(cuò)誤。
舉個(gè)栗子
在遍歷一個(gè)List同時(shí)remove其中的某個(gè)item
package com.example;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MyClass {
static List<String> mList = new ArrayList<>();
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.initData();
//forEach遍歷同時(shí)remove某個(gè)item
for (String s : mList) {
if ("aaa".equals(s)) {
mList.remove(s);
}
}
myClass.iterator(mList);
//模擬forEach遍歷同時(shí)remove某個(gè)item
Iterator<String> iterator = mList.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if ("aaa".equals(s)) {
mList.remove(s);
}
}
myClass.iterator(mList);
//iterator遍歷同時(shí)remove某個(gè)item
Iterator<String> iterator1 = mList.iterator();
while (iterator1.hasNext()) {
String s = iterator1.next();
if ("aaa".equals(s)) {
iterator1.remove();
}
}
myClass.iterator(mList);
//for遍歷同時(shí)remove某個(gè)item
for (int i = 0; i < mList.size(); i++) {
String s = mList.get(i);
if ("aaa".equals(s)) {
mList.remove(s);
}
}
myClass.iterator(mList);
}
public void initData() {
mList.add("aaa");
mList.add("bbb");
mList.add("ccc");
mList.add("ddd");
}
public void iterator(List<String> list) {
for (String s : list) {
System.out.println(s+" ");
}
}
}
分別用這四種實(shí)現(xiàn)邏輯去遍歷(注釋其它的三種實(shí)現(xiàn))缠劝,打印結(jié)果依次是:
分析問題
從打印的結(jié)果我們可以看到前面兩種實(shí)現(xiàn)會(huì)報(bào)ConcurrentModificationException 潮梯,其實(shí)這兩種實(shí)現(xiàn)本質(zhì)是一種,第一種forEach的方式其實(shí)是在第二種iterator上包了一層語(yǔ)法糖惨恭。第三種實(shí)現(xiàn)是iterator的正確實(shí)現(xiàn)方式秉馏,對(duì)比一下第二種,區(qū)別只是在mList.remove和iterator.remove喉恋,OK沃饶,這樣就成功的縮小了問題范圍。接下來轻黑,直接看源碼:
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
@SuppressWarnings("unchecked")
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;
return (E) elementData[lastRet = i];
}
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();
}
}
}
重點(diǎn)看remove方法糊肤,ArrayList.this.remove(lastRet)之后,有一個(gè)expectedModCount = modCount氓鄙。再看next()方法的checkForComodification():
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
問題就出在這里馆揉,iterator的remove方法有一個(gè)同步計(jì)數(shù)的邏輯。
總結(jié)
在遍歷List的時(shí)候抖拦,如果對(duì)List數(shù)據(jù)有改動(dòng)升酣,不應(yīng)用forEach的遍歷方式,可以用for或者iterator來替代态罪。