Ako sa vyhnúť výnimke ConcurrentModificationException pri prechádzaní kolekcie?
sobes.tech AI
Odpoveď od AI
Aby ste sa vyhli ConcurrentModificationException pri prechádzaní kolekcie, môžete použiť nasledujúce prístupy:
-
Používajte vlákno-bezpečné kolekcie:
java.util.concurrentposkytuje vlákno-bezpečné alternatívy štandardných kolekcií. Napríklad:CopyOnWriteArrayListnamiestoArrayListCopyOnWriteArraySetnamiestoHashSetConcurrentHashMapnamiestoHashMap
// Príklad s CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList; import java.util.Iterator; public class ConcurrentCollectionExample { public static void main(String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); list.add("A"); list.add("B"); list.add("C"); // Iterátor nehodí `ConcurrentModificationException` Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); // Bezpečne meniť kolekciu počas prechádzania if (element.equals("B")) { list.remove(element); } } System.out.println("Konečný zoznam: " + list); } } -
Zamknite kolekciu: Synchronizujte prístup ku kolekcii pomocou
synchronizedalebo zámkovjava.util.concurrent.locks.// Príklad so synchronizovaným blokom import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class SynchronizedCollectionExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); synchronized (list) { // Zamknutie kolekcie pre prístup Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); // Nie je povolené meniť kolekciu tu, ak iný vlákno môže získať prístup // cez tento `synchronized` blok } } // Zmena kolekcie mimo synchronizovaného bloku alebo iným vláknom // môže stále spôsobiť `ConcurrentModificationException` pri paralelnom prechádzaní } } -
Používajte iterátor na odstraňovanie prvkov: Ak potrebujete odstraňovať prvky počas prechádzania, použite metódu
remove()samotného iterátora (ak ho kolekcia podporuje).// Príklad odstraňovania pomocou `Iterator.remove()` import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class IteratorRemoveExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); // Bezpečne odstrániť prvok cez iterátor if (element.equals("B")) { iterator.remove(); } } System.out.println("Konečný zoznam: " + list); } } -
Prechádzajte kópiu kolekcie: Vytvorte kópiu kolekcie a prechádzajte ju. Zmeny v pôvodnej kolekcii nebudú mať vplyv na prechádzanie kópie.
// Príklad prechádzania kópie import java.util.ArrayList; import java.util.List; public class CopyIterationExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("A"); list.add("B"); list.add("C"); // Vytvorenie kópie na prechádzanie List<String> copyList = new ArrayList<>(list); for (String element : copyList) { System.out.println(element); // Môžeme meniť pôvodnú kolekciu počas prechádzania kópie if (element.equals("B")) { list.remove(element); } } System.out.println("Konečný zoznam: " + list); } }
Výber prístupu závisí od konkrétnych požiadaviek: či je potrebné meniť kolekciu počas prechádzania, aký je význam výkonu a či kód beží v viacvláknovej prostredí. CopyOnWriteArrayList/CopyOnWriteArraySet sú vhodné, keď je čítanie výrazne väčšie ako zápis. Zamknutie je vhodnejšie, keď sa zmeny dejú zriedkavo, ale môžu byť častejšie než pri použití CopyOnWrite. Použitie Iterator.remove() alebo prechádzanie kópie je vhodné, keď sa zmeny dejú iba v rámci jednej iterácie alebo vlákna.