I believe that anyone who has written some Java code has encountered this exception, which is generally caused by the following code:
import java.util.List; import java.util.ArrayList; public class Test{ public static void main(String[] args){ List<String> list = new ArrayList<>(); list.add("123"); list.add("456"); list.add("789"); for(String obj : list){ list.remove(obj); } } }
The above code will eventually throw java.util.ConcurrentModificationException, so why? First, we decompile the above code and get the following results (you can ignore it if you know the foreach syntax sugar better):
public class Test { public Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 4: 0 public static void main(java.lang.String[]); Code: 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: ldc #4 // String 123 11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 16: pop 17: aload_1 18: ldc #6 // String 456 20: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 25: pop 26: aload_1 27: ldc #7 // String 789 29: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 34: pop 35: aload_1 36: invokeinterface #8, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 41: astore_2 42: aload_2 43: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 48: ifeq 72 51: aload_2 52: invokeinterface #10, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 57: checkcast #11 // class java/lang/String 60: astore_3 61: aload_1 62: aload_3 63: invokeinterface #12, 2 // InterfaceMethod java/util/List.remove:(Ljava/lang/Object;)Z 68: pop 69: goto 42 72: return LineNumberTable: line 6: 0 line 7: 8 line 8: 17 line 9: 26 line 10: 35 line 11: 61 line 12: 69 line 13: 72 }
Translating the above code is equivalent to the following code:
import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class Test{ public static void main(String[] args){ List<String> list = new ArrayList<>(); list.add("123"); list.add("456"); list.add("789"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ String obj = iterator.next(); list.remove(obj); } } }
Then we Looking at the iterator.hasNext()
source code, you can find that the first line calls the checkForComodification
method. Let’s check this method:
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
in modCount != expectedModCount
When this condition is established, a ConcurrentModificationException
exception will be thrown. So how is this condition established?
1. First, we check the source of modCount
and we can find that the value of modCount
is equal to the size
of the current List. When calling List When using the .remove
method, modCount
will also be reduced by 1 accordingly;
2. Then we check the source of expectedModCount
, and we can see that it is constructing# When ##Iterator (the internal implementation of ArrayList is used here), there is a variable assignment, and the value of
modCount is assigned to
expectedModCount;
List.remove method,
modCount changes but
expectedModCount does not change. When the first loop ends, it is deleted. When a data is ready to call the
iterator.hasNext() method for the second time in a loop, the
checkForComodification() method will throw an exception, because at this time the
List
modCount has changed to 2, and
expectedModCount is still 3, so
ConcurrentModificationException exception will be thrown;
java.util.ArrayList.Itr (Iterator implementation in ArrayList), we can find that there is a
remove method in this iterator that can delete the current iteration element, and it will Modify
modCount and
expectedModCount at the same time, so that no exception will be thrown when performing the
checkForComodification check. The source code of the
remove method is as follows :
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(); } }Among them,
ArrayList.this.remove(lastRet);This line will change the value of
modCount, and the subsequent modification will be synchronously
expectedModCount## The value of # is equal to the value of modCount
; Now modify the program we started with as follows and it will run normally:
import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class Test{ public static void main(String[] args){ List<String> list = new ArrayList<>(); list.add("123"); list.add("456"); list.add("789"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { System.out.println("移除:" + iterator.next()); iterator.remove(); } } }
The above is the detailed content of How to solve the ConcurrentModificationException exception warning in java. For more information, please follow other related articles on the PHP Chinese website!