1. Mito
2. Overview
2.1 Operation (errors are reported in all three cases)
2.1.1 The first error condition
import java.util.*;
public class T {
public static void main(String[] args) {
List<String> myList = new ArrayList<String>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
if (value.equals( "3")) {
myList.remove(value); // error
}
}
}
}
2.1.2 The second error condition
import java.util.*;
public class T {
public static void main(String[] args) {
List<String> myList = new ArrayList<String>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");
for (Iterator<String> it = myList.iterator(); it.hasNext();) {
String value = it.next();
if (value.equals( "3")) {
myList.remove(value); // error
}
}
}
}
2.1.2 The third error condition
import java.util.*;
public class T {
public static void main(String[] args) {
List<String> myList = new ArrayList<String>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");
for (Iterator<String> it = myList.iterator(); it.hasNext();) {
String value = it.next();
if (value.equals( "3")) {
myList.remove(value); // error
}
}
}
}
2.2. Phenomenon (error message)
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.ejudata.platform.test.T.main(T.java:14)
3. Reason
The above three cases are traversal using Iterator, and all delete operations are performed through ArrayList.remove (Object)
List、Set、Map 都可以通过Iterator进行遍历,在使用其他集合遍历时进行增删操作都需要留意是否会触发ConcurrentModificationException异常。
Through the following ArrayList source code, you can know that List maintains such a traversal modcount, used to record the number of times to modify the data structure in List and Iterator.
When deleting an element, the number of modcounts in the List will decrease by 1, but the number of Iterators does not decrease. Once the number of the two modcounts is not equal, a ConcurrentModificationException error will be reported.
4. ArrayList source code
public class ArrayList<E> extends AbstractList<E>
implements Cloneable, Serializable, RandomAccess {
@Override
public boolean remove(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++; // 只要删除成功都是累加
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++; // 只要删除成功都是累加
return true;
}
}
}
return false;
}
@Override public Iterator<E> iterator() {
return new ArrayListIterator();
}
private class ArrayListIterator implements Iterator<E> {
......
// 全局修改总数保存到当前类中
/** The expected modCount value */
private int expectedModCount = modCount;
@SuppressWarnings("unchecked")
public E next() {
ArrayList<E> ourList = ArrayList.this;
int rem = remaining;
// 如果创建时的值不相同,抛出异常
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (rem == 0) {
throw new NoSuchElementException();
}
remaining = rem - 1;
return (E) ourList.array[removalIndex = ourList.size - rem];
}
//其余代码不在这里显示
}
}
5. Solve
5.1 Option 1: Use the remove method provided by Iterator to delete the current element
import java.util.*;
public class T {
public static void main(String[] args) {
List<String> myList = new ArrayList<String>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");
for (Iterator<String> it = myList.iterator(); it.hasNext();) {
String value = it.next();
if (value.equals( "3")) {
it.remove(); // ok
}
}
System. out.println( "List Value:" + myList.toString());
}
}
5.2 Scheme 2: Build a collection, record the elements that need to be deleted, and then delete them uniformly
import java.util.*;
public class T {
public static void main(String[] args) {
List<String> myList = new ArrayList<String>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");
List<String> templist = new ArrayList<String>();
for (String value : myList) {
if (value.equals( "3")) {
templist.add(value);
}
}
// 可以查看removeAll源码,其中使用Iterator进行遍历
myList.removeAll(templist);
System. out.println( "List Value:" + myList.toString());
}
5.3 Scenario 3: Do not use Iterator for traversal, you need to pay attention to ensure that the index is normal
import java.util.*;
public class T {
public static void main(String[] args) {
List<String> myList = new ArrayList<String>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");
for ( int i = 0; i < myList.size(); i++) {
String value = myList.get(i);
System. out.println( "List Value:" + value);
if (value.equals( "3")) {
myList.remove(value); // ok
i--; // 因为位置发生改变,所以必须修改i的位置
}
}
System. out.println( "List Value:" + myList.toString());
}
}
5.4 Scenario 4: Operation in multiple threads
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class T {
public static void main(String[] args) {
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");
new Thread(new Runnable() {
@Override
public void run() {
for (String string : myList) {
System.out.println("遍历集合 value = " + string);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < myList.size(); i++) {
String value = myList.get(i);
System.out.println("删除元素 value = " + value);
if (value.equals( "3")) {
myList.remove(value);
i--; // 注意
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}