public class ArraylistRemoveTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
remove(list,"b");
}
public static void print( List<String> list){
for (String item:list) {
System.out.println("list:item"+item);
}
}
public static void remove(List<String> list,String target){
for (String item:list) {
if(item.equals(target)){
list.remove(item);
}
}
print(list);
}
}
执行 方法,出现如下错误:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.ips.list.ArrayListRemove.remove21(ArrayListRemove.java:82)
at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)
产生java.util.ConcurrentModificationException异常。foreach 写法实际上是对的 Iterable、hasNext、next方法的简写。因此我们从List.iterator()着手分析,跟踪iterator()方法,该方法返回了 Itr 迭代器对象。
public Iterator<E> iterator() {
return new Itr();
}
Itr 类定义代码:
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];
}
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();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
通过代码我们发现 Itr 是 ArrayList 中定义的一个私有内部类,在 next、remove方法中都会调用 checkForComodification 方法,该方法的作用是判断 modCount != expectedModCount是否相等,如果不相等则抛出ConcurrentModificationException异常。每次正常执行 remove 方法后,都会对执行expectedModCount = modCount赋值,保证两个值相等,那么问题基本上已经清晰了,在 foreach 循环中执行 list.remove(item);,对 list 对象的 modCount 值进行了修改,而 list 对象的迭代器的 expectedModCount 值未进行修改,因此抛出了ConcurrentModificationException异常。
二:先获取list的size,删除后导致size变化,索引越界 失败
public static void remove2(List<String> list,String target){
int size = list.size();
for(int i= 0;i<size;i++){
if(list.get(i).equals(target)){
list.remove(target);
}
}
print(list);
}
Exception in thread “main” java.lang.IndexOutOfBoundsException: Index: 5, Size: 5
at java.util.ArrayList.rangeCheck(ArrayList.java:635)
at java.util.ArrayList.get(ArrayList.java:411)
at com.ips.list.ArrayListRemove.remove11(ArrayListRemove.java:33)
at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)
由于int size = list.size();提前获取了 List 的大小,for 循环中删除了两个元素,导致出现数组越界问题。
三:使用迭代器的iterator.remove()删除成功
public static void remove4(List<String> list,String target){
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String next = iterator.next();
if(target.equals(next)){
iterator.remove();
}
}
print(list);
}
四 modCount与iterator的数量不同 失败
public static void remove4(List<String> list,String target){
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String next = iterator.next();
if(target.equals(next)){
list.remove(next);
}
}
print(list);
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.cainiao.product.dwork.built.starter.metaq.ArraylistRemoveTest.remove4(ArraylistRemoveTest.java:66)
at com.cainiao.product.dwork.built.starter.metaq.ArraylistRemoveTest.main(ArraylistRemoveTest.java:17)
Disconnected from the target VM, address: '127.0.0.1:59414', transport: 'socket'
Process finished with exit code 1
五:CopyOnWriteArrayList 成功
public static void remove5(List<String> list,String target){
final CopyOnWriteArrayList<String> copyList = new CopyOnWriteArrayList<>(list);
for (String item:copyList) {
if(item.equals(target)){
copyList.remove(item);
}
}
print(copyList);
}