J'ai ce petit morceau de code et cela me donne l'exception de modification simultanée. Je ne comprends pas pourquoi je continue de l'obtenir, même si je ne vois aucune modification simultanée en cours.
import Java.util.*;
public class SomeClass {
public static void main(String[] args) {
List<String> s = new ArrayList<>();
ListIterator<String> it = s.listIterator();
for (String a : args)
s.add(a);
if (it.hasNext())
String item = it.next();
System.out.println(s);
}
}
Pour éviter le ConcurrentModificationException
, vous devez écrire votre code comme ceci:
import Java.util.*;
public class SomeClass {
public static void main(String[] args) {
List<String> s = new ArrayList<String>();
for(String a : args)
s.add(a);
ListIterator<String> it = s.listIterator();
if(it.hasNext()) {
String item = it.next();
}
System.out.println(s);
}
}
UNE Java.util.ListIterator
vous permet de modifier une liste pendant l'itération, mais pas entre sa création et son utilisation.
Je ne comprends pas pourquoi je continue de l'obtenir, même si je ne vois aucune modification simultanée en cours.
Entre la création de l'itérateur et le début d'utilisation de l'itérateur, vous avez ajouté des arguments à la liste à itérer. Il s'agit d'une modification simultanée.
ListIterator<String> it = s.listIterator();
for (String a : args)
s.add(a); // concurrent modification here
if (it.hasNext())
String item = it.next(); // exception thrown here
Créez l'itérateur APRÈS avoir terminé d'ajouter des éléments à la liste:
for (String a : args)
s.add(a);
ListIterator<String> it = s.listIterator();
if (it.hasNext())
String item = it.next();
D'après JavaDoc: pour ConcurrentModificatoinException: "il n'est généralement pas autorisé pour un thread de modifier une collection pendant qu'un autre thread l'itère".
Cela signifie simplement que si vous avez encore un itérateur ouvert, vous n'êtes pas autorisé à modifier la liste car la boucle de l'itérateur se rompra. Essayez de déplacer ListIterator<String> it = s.listIterator();
jusqu'après la boucle for.
Vous n'êtes pas autorisé à continuer l'itération sur un itérateur après la modification de la liste sous-jacente. Ici, vous créez l'itérateur avant d'ajouter quelques éléments à s
, puis procédez à une hasNext()
et une next()
dessus après les ajouts, conduisant à la ConcurrentModificationException
Si les solutions ci-dessus ne fonctionnent pas correctement. Vous pouvez utiliser l'ancienne boucle for pour itérer une liste en ajoutant de nouveaux éléments. Voir l'exemple ci-dessous:
import Java.util.*;
public class SomeClass {
public static void main(String[] args) {
ArrayList<AClass> aList = new ArrayList<AClass>(); // we will iterate this
// this will cause ConcurrentModificationException.
// Since we are iterating the list, at the same time modifying it.
/*for(AClass a: aList){
aList.add(someMethod(a));
}*/
// old fashion for-loop will help
int limit = aList.size();
for(int i=0; ctr<limit; ++i){
AClass a = aList.get(i);
aList.add(someMethod(a));
}
}
}
ConcurrentModificationException peut survenir à la fois dans un environnement à thread unique et dans un environnement à plusieurs threads . Le principal problème est que tous les itérateurs à usage général (comme celui utilisé dans ArrayList) sont tous les itérateurs FailFast , ce qui échoue lorsque nous essayons de modifier une liste si un itérateur itère déjà dessus. Solution -> Utilisez CopyOnWriteArrayList si un tel scénario est requis par l'exigence plutôt que d'utiliser ArrayList.
Pour une démonstration complète de cela, le code mentionné ci-dessous peut être utilisé. Nous avons juste besoin de changer l'implémentation de CopyOnWriteArrayList en ArrayList.
import Java.util.ArrayList;
import Java.util.Iterator;
import Java.util.List;
import Java.util.concurrent.CopyOnWriteArrayList;
/**
* @author narif
*
*/
public class TestApp {
/**
* @param args
*/
public static void main(String[] args) {
List<String> testList = new ArrayList<>();
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add(6, "abcAtindex6");
int size = testList.size();
System.out.println("The Current List (ArrayList) is: " + testList);
System.out.println("The size of the List (ArrayList) is: " + size);
/* Comment the below lines to get the ConcurrentModificationException */
testList = new CopyOnWriteArrayList<>(testList);
for (String value : testList) {
System.out.println("The Value from ForEach Loop is: " + value);
/*
* Concurrent modification is happening here
* One iterator is iterating over the list while we are trying to add new values to
* the list so the results of the iteration are undefined under these circumstances.
* So teh fail fast iterators will fail and will throw the ConcurrentModificationException.
*/
testList.add("valueFromForLoop");
testList.add("anotherValueFromForEachLoop");
}
Iterator<String> it = testList.iterator();
while (it.hasNext()) {
String abc = it.next();
System.out.println(abc);
testList.add("Value from Iterator1");
testList.add("Value from Iterator2");
testList.add("Value from Iterator3");
testList.add("Value from Iterator4");
}
System.out.println("Did the modificationa and all after conevrting the ArrayList to CopyOnWriteArrayList.");
System.out.println("Calling the method to get the new List..");
testList = new CopyOnWriteArrayList<>(getTheList(testList));
for (String value : testList) {
System.out.println("The value returned from method is : " + value);
}
}
private static List<String> getTheList(List<String> pList) {
List<String> list = new CopyOnWriteArrayList<>(pList);
int i = 0;
for (String lValue : list) {
System.out.println("The list Passed is " + list);
i++;
list.add("localVaueFromMethod" + i);
list.removeAll(pList);
}
return list;
}
}
Pour plus d'informations, suivez ce lien, cela peut être très utile ConcurrentModificationException Java Docs
pour comprendre cela, regardons la source de l'implémentation de HashMap:
public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable{
qui contient HashIterator comme ci-dessous:
private abstract class HashIterator {
...
int expectedModCount = modCount;
...
HashMapEntry<K, V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
....
}
chaque fois que vous créez un itérateur:
pour éviter cela, vous pouvez:
cela vous permettra d'itérer et d'ajouter ou de supprimer des éléments en même temps sans lever une exception
L'itérateur de carte/liste de concurrence est un itérateur "faiblement cohérent" qui ne lèvera jamais ConcurrentModificationException, et garantit de parcourir les éléments tels qu'ils existaient lors de la construction de l'itérateur, et peut (mais n'est pas garanti) refléter toutes les modifications ultérieures à la construction.
Cela n'a pas fonctionné:
LinkedList<String> linkedList = new LinkedList<String>();
ListIterator listIterator = linkedList.listIterator();
linkedList.add("aa");
linkedList.add("bb");
Cela a fonctionné:
LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("aa");
linkedList.add("bb");
ListIterator listIterator = linkedList.listIterator();
Jetez un œil à la page Oracle documentation .
public class ConcurrentModificationException
extends RuntimeException
Cette exception peut être levée par des méthodes qui ont détecté une modification simultanée d'un objet lorsqu'une telle modification n'est pas autorisée
Notez que cette exception n'indique pas toujours qu'un objet a été modifié simultanément par un thread différent. Si un seul thread émet une séquence d'appels de méthode qui viole le contrat d'un objet, l'objet peut lever cette exception. Par exemple, si un thread modifie directement une collection pendant qu'il itère sur la collection avec un itérateur à échec rapide, l'itérateur lèvera cette exception .
Dans votre cas, vous avez modifié la collection après avoir créé l'itérateur et vous avez donc rencontré l'exception.
Si vous modifiez votre code selon la réponse Stephen C, vous n'obtiendrez pas cette erreur.