Как да избегнете изключението ConcurrentModificationException при обхождане на колекция?
sobes.tech AI
Отговор от AI
За да избегнете ConcurrentModificationException при обхождане на колекция, можете да използвате следните подходи:
-
Използвайте потокобезопасни колекции:
java.util.concurrentпредоставя потокобезопасни алтернативи на стандартните колекции. Например:CopyOnWriteArrayListвместоArrayListCopyOnWriteArraySetвместоHashSetConcurrentHashMapвместоHashMap
// Пример с 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"); // Итераторът не хвърля ConcurrentModificationException Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); // Безопасно е да променяте колекцията по време на обхождане if (element.equals("B")) { list.remove(element); } } System.out.println("Крайният списък: " + list); } } -
Блокирайте колекцията: Синхронизирайте достъпа до колекцията с помощта на
synchronizedили блокировкиjava.util.concurrent.locks.// Пример с синхронизирана блокировка 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) { // Блокираме колекцията за достъп Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); // Не е позволено да променяте колекцията тук, ако друг поток може да получи достъп // чрез същия `synchronized` блок } } // Промяната в колекцията извън синхронизирания блок или от друг поток // може все още да предизвика ConcurrentModificationException при паралелно обхождане } } -
Използвайте итератор за изтриване на елементи: Ако трябва да изтриете елементи по време на обхождане, използвайте метода
remove()на самия итератор (ако колекцията го поддържа).// Пример за изтриване с използване на 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); // Безопасно е да изтриете елемент чрез итератора if (element.equals("B")) { iterator.remove(); } } System.out.println("Крайният списък: " + list); } } -
Обхождайте копие на колекцията: Създайте копие на колекцията и я обхождайте. Промените в оригиналната колекция няма да повлияят на обхождането на копието.
// Пример за обхождане на копие 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"); // Създаваме копие за обхождане List<String> copyList = new ArrayList<>(list); for (String element : copyList) { System.out.println(element); // Можем да променяме оригиналната колекция по време на обхождане на копието if (element.equals("B")) { list.remove(element); } } System.out.println("Крайният списък: " + list); } }
Изборът на подход зависи от конкретните изисквания: трябва ли да променяте колекцията по време на обхождане, колко е важна производителността и дали кодът работи в многонишкова среда. CopyOnWriteArrayList/CopyOnWriteArraySet са подходящи, когато четенето е значително по-голямо от записите. Блокирането е подходящо, когато промените се случват по-рядко, но могат да бъдат по-чести отколкото при използване на CopyOnWrite. Използването на Iterator.remove() или обхождането на копие е подходящо, когато промените се случват само в рамките на една итерация или поток.