J'essaie de supprimer certains éléments d'un ArrayList
en le répétant comme suit:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Bien sûr, je reçois un ConcurrentModificationException
lorsque je tente de supprimer des éléments de la liste en même temps que je répète myArrayList
. Existe-t-il une solution simple pour résoudre ce problème?
Comme alternative aux réponses des autres, j'ai toujours fait quelque chose comme ça:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
Cela vous évitera d'avoir à traiter directement avec l'itérateur, mais nécessite une autre liste. J'ai toujours préféré cette route pour une raison quelconque.
L'utilisateur Java 8 peut le faire: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
Cela supprimera des éléments de la liste pour lesquels certaines conditions sont satisfaites
Vous devez utiliser la méthode remove () de l'itérateur, ce qui signifie qu'aucune boucle for améliorée:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
Non non Non!
Dans les tâches simples traitées, vous n'avez pas besoin d'utiliser Iterator, par ailleurs, CopyOnWriteArrayList (en raison de l'impact négatif sur les performances).
La solution est beaucoup plus simple: essayez d'utiliser la boucle for canonique au lieu de la boucle for-each .
Selon Java détenteurs des droits d'auteur (il y a quelques années, Sun, maintenant Oracle) pour chaque guide de boucle , il utilise un itérateur pour parcourir la collection et le cache pour améliorer l'apparence du code. Malheureusement, comme nous pouvons le constater, cela a engendré plus de problèmes que de profits, sinon ce sujet ne se poserait pas.
Par exemple, ce code mènera à une exception Java.util.ConcurrentModificationException lors de la prochaine itération sur la liste de tableaux modifiée:
// process collection
for (SomeClass currElement: testList) {
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
}
}
Mais le code suivant fonctionne très bien:
// process collection
for (int i = 0; i < testList.size(); i++) {
SomeClass currElement = testList.get(i);
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
i--; //to avoid skipping of shifted element
}
}
Essayez donc d'utiliser l'approche d'indexation pour parcourir les collections et évitez les boucles pour chaque boucle, car elles ne sont pas équivalentes! For-each boucle utilise des itérateurs internes, qui vérifient la modification de la collection et lèvent une exception ConcurrentModificationException. Pour confirmer cela, examinez de plus près le tracé de la pile imprimé lorsque vous utilisez le premier exemple que j'ai posté:
Exception in thread "main" Java.util.ConcurrentModificationException
at Java.util.AbstractList$Itr.checkForComodification(AbstractList.Java:372)
at Java.util.AbstractList$Itr.next(AbstractList.Java:343)
at TestFail.main(TestFail.Java:43)
Pour le multithreading, utilisez les approches multitâches correspondantes (telles que le mot clé synchronisé).
Tandis que les autres solutions suggérées fonctionnent, si vous voulez vraiment que la solution soit sécurisée pour le thread, vous devez remplacer ArrayList par CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception
List<String> s = new CopyOnWriteArrayList<>();
s.add("B");
Iterator<String> it = s.iterator();
s.add("A");
//Below removes only "B" from List
while (it.hasNext()) {
s.remove(it.next());
}
System.out.println(s);
Une autre méthode consiste à convertir votre List
en array
, à les itérer et à les supprimer directement de la List
en fonction de votre logique.
List<String> myList = new ArrayList<String>(); // You can use either list or set
myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");
Object[] obj = myList.toArray();
for(Object o:obj) {
if(condition)
myList.remove(o.toString());
}
Si vous souhaitez modifier votre liste pendant le parcours, vous devez utiliser le paramètre Iterator
. Et vous pouvez ensuite utiliser iterator.remove()
pour supprimer les éléments pendant le parcours.
List myArrayList = Collections.synchronizedList(new ArrayList());
//add your elements
myArrayList.add();
myArrayList.add();
myArrayList.add();
synchronized(myArrayList) {
Iterator i = myArrayList.iterator();
while (i.hasNext()){
Object object = i.next();
}
}
Vous pouvez utiliser la fonction iterator remove () pour supprimer l'objet de l'objet de collection sous-jacent. Mais dans ce cas, vous pouvez supprimer le même objet et aucun autre objet de la liste.
de ici