[Colección] ConcurrentModificationException de Java Collection

1. Mito

Inserte la descripción de la imagen aquí

2. Descripción general

2.1 Operación (se informan errores en los tres casos)

2.1.1 La primera condición de error

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 La segunda condición de error

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 La tercera condición de error

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 Fenómeno (mensaje de error)

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. Razón

Los tres casos anteriores son transversales utilizando Iterator, y todas las operaciones de eliminación se realizan a través de ArrayList.remove (Object)

List、Set、Map 都可以通过Iterator进行遍历,在使用其他集合遍历时进行增删操作都需要留意是否会触发ConcurrentModificationException异常。

A través del siguiente código fuente de ArrayList, puede saber que List mantiene un modcount transversal tal, que se utiliza para registrar la cantidad de veces que se modifica la estructura de datos en List e Iterator.

Al eliminar un elemento, el número de modcounts en la Lista disminuirá en 1, pero el número de iteradores no disminuirá. Una vez que el número de los dos modcounts no sea igual, se informará un error ConcurrentModificationException.

4. Código fuente de ArrayList

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. Resolver

5.1 Opción 1: utilice el método de eliminación proporcionado por Iterator para eliminar el elemento actual

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 Esquema 2: cree una colección, registre los elementos que deben eliminarse y luego elimínelos de manera uniforme

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 Escenario 3: no use Iterator para el recorrido, debe prestar atención para asegurarse de que el índice sea 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 Escenario 4: Operación en múltiples hilos

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();
    }
}
1228 artículos originales publicados · elogiados 463 · 1.57 millones de visitas

Supongo que te gusta

Origin blog.csdn.net/qq_21383435/article/details/105499725
Recomendado
Clasificación