问题分析
当我们对ArrayList直接操作remove操作时,可能会抛出java.util.ConcurrentModificationException操作,如下:
List<String> list = new ArrayList<>();
list.add("key1");
list.add("key2");
list.add("key3");
for (String s : list) {
if (s.equals("key1")) {
list.remove(s);
}
}
运行报如下错误:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
首先分析下为什么汇报这个错误呢,根据上面的异常堆栈信息可知是再调用itr.next的checkForComodification方法出的错,查看jdk源码如下
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
更加checkForComodification方法的源码可知是由于不满足条件modCount != expectedModCount,再itr的初始化时我们看到int expectedModCount = modCount;也就是expectedModCount 和modCount的初始值都是3,而再调用list.remove(s)时,remove的源码如下:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
可以看到remove中的fastRemove对modCount变量做了一次modCount++,从而导致modCount的值变为4,而expectedModCount值不变,仍然为3,故抛出了ConcurrentModificationException异常。
解决方案一
采用迭代器对ArrayList进行remove操作,具体代码如下:
List<String> list = new ArrayList<>();
list.add("key1");
list.add("key2");
list.add("key3");
Iterator<String> iterable = list.iterator();
while (iterable.hasNext()) {
String key = iterable.next();
if (key.equals("key1")) {
iterable.remove();
}
}
for (String s : list) {
System.out.println(s);
}
输出结果如下:
解决方案一
采用线程安全的CopyOnWriteArrayList,输出结果同上
List<String> list = new CopyOnWriteArrayList<>();
list.add("key1");
list.add("key2");
list.add("key3");
for (String s : list) {
if (s.equals("key1")) {
list.remove(s);
}
}
for (String s : list) {
System.out.println(s);
}
阅读资料
java.util.ConcurrentModificationException详解
java.util.ConcurrentModificationException