[Collection] ConcurrentModificationException of Java Collection

1. Mito

Insert picture description here

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();
    }
}
Published 1228 original articles · praised 463 · 1.57 million views

Guess you like

Origin blog.csdn.net/qq_21383435/article/details/105499725
Recommended