Maison >Java >javaDidacticiel >Comment résoudre l'avertissement d'exception ConcurrentModificationException en Java
Je crois que quiconque a écrit du code Java a rencontré cette exception, qui est généralement causée par le code suivant :
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); } } }
Le code ci-dessus finira par provoquer une exception java.util.ConcurrentModificationException, alors pourquoi ? Tout d'abord, nous décompilons le code ci-dessus et obtenons les résultats suivants (vous pouvez l'ignorer si vous connaissez mieux la syntaxe foreach sugar) :
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 }
La traduction du code ci-dessus équivaut au code suivant :
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); } } }
Ensuite, nous regardons iterator.hasNext( )
, vous pouvez constater que la première ligne appelle la méthode checkForComodification
Vérifions cette méthode : iterator.hasNext()
源码,可以发现第一行调用了checkForComodification
方法,我们查看这个方法:
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
在modCount != expectedModCount
这个条件成立的时候会抛出ConcurrentModificationException
异常,那么这个条件是怎么成立的呢?
1、首先我们查看modCount
的来源,可以发现modCount
的值等于当前List的size
,当调用List.remove
方法的时候modCount
也会相应的减1;
2、然后我们查看expectedModCount
的来源,可以看到是在构造Iterator
(这里使用的是ArrayList的内部实现)的时候,有一个变量赋值,将modCount
的值赋给了expectedModCount
;
3、最后当我们执行循环调用List.remove
方法的时候,modCount
改变了但是expectedModCount
并没有改变,当第一次循环结束删除一个数据准 备第二次循环调用iterator.hasNext()
方法的时候,checkForComodification()
方法就会抛出异常,因为此时List
的modCount
已经变为 了2,而expectedModCount
仍然是3,所以会抛出ConcurrentModificationException
异常;
那么如何解决该问题呢?我们查看java.util.ArrayList.Itr
(ArrayList中的Iterator实现)的源码可以发现,在该迭代器中有一个remove
方法可以 删除当前迭代元素,而且会同时修改modCount
和expectedModCount
,这样在进行checkForComodification
检查的时候就不会抛出异常了,该remove
方法源码如下:
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(); } }
其中ArrayList.this.remove(lastRet);
这一行会改变modCount
的值,而后边会同步的修改expectedModCount
的值等于modCount
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(); } } }Lorsque la condition
modCount != ExpectModCount.
est établi, il lancera une exception ConcurrentModificationException
, alors comment cette condition est-elle établie ? 1. Tout d'abord, nous vérifions la source de modCount
et nous pouvons constater que la valeur de modCount
est égale à la taille
du courant. List. Lors de l'appel de List. Lors de l'utilisation de la méthode Remove
, modCount
sera également réduit de 1 en conséquence 🎜🎜2. Ensuite, nous vérifions la source de expectedModCount et nous pouvons voir qu'il construit <code> Lorsque Iterator
(l'implémentation interne de ArrayList est utilisée ici), il y a une affectation de variable et la valeur de modCount
est attribué à expectedModCount
; 🎜🎜3 Enfin, lorsque nous exécutons la boucle et appelons la méthode List.remove
, modCount
change mais . ExpectModCount
ne change pas Lorsque la première boucle se termine, elle est supprimée Lorsqu'une donnée est prête à appeler la méthode iterator.hasNext()
pour la deuxième fois dans une boucle, le iterator.hasNext()
est supprimé. La méthode code>checkForComodification() lèvera une exception, car à ce moment la List
Le modCount
du code> est passé à 2 et le ExpectModCount
est toujours 3, donc l'exception ConcurrentModificationException
sera levée 🎜🎜🎜🎜🎜🎜Méthode de solution🎜🎜Alors, comment résoudre ce problème ? Lorsque nous examinons le code source de java.util.ArrayList.Itr
(implémentation de l'itérateur dans ArrayList), nous pouvons constater qu'il existe une méthode remove
dans cet itérateur qui peut supprime l'élément d'itération actuel. De plus, modCount
et expectedModCount
seront modifiés en même temps, de sorte qu'aucune exception ne sera levée lors de l'exécution de la checkForComodification
. > vérifiez. Le remove Le code source de la méthode est le suivant : 🎜rrreee🎜 Parmi eux, <code>ArrayList.this.remove(lastRet);
changera la valeur de modCount, et sera modifié de manière synchrone plus tard expectedModCount
est égale à la valeur de modCount
🎜🎜Maintenant, modifiez le programme avec lequel nous avons commencé ; suit et il fonctionnera normalement : 🎜rrreeeCe qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!