Sobes.tech
Middle

Cum să eviți excepția ConcurrentModificationException în timpul parcurgerii unei colecții?

sobes.tech AI

Răspuns de la AI

Pentru a evita ConcurrentModificationException în timpul iterării peste o colecție, se pot folosi următoarele abordări:

  1. Utilizarea colecțiilor sigure pentru fire de execuție: java.util.concurrent oferă alternative sigure pentru colecțiile standard. De exemplu:

    • CopyOnWriteArrayList în loc de ArrayList
    • CopyOnWriteArraySet în loc de HashSet
    • ConcurrentHashMap în loc de HashMap
    // Exemplu cu 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");
    
            // Iterator nu aruncă `ConcurrentModificationException`
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String element = iterator.next();
                System.out.println(element);
                // Modificarea colecției în timpul iterării este sigură
                if (element.equals("B")) {
                    list.remove(element);
                }
            }
            System.out.println("Lista finală: " + list);
        }
    }
    
  2. Blocarea colecției: Sincronizați accesul la colecție folosind synchronized sau blocuri de blocare java.util.concurrent.locks.

    // Exemplu cu bloc sincronizat
    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) { // Blocăm colecția pentru acces
               Iterator<String> iterator = list.iterator();
               while (iterator.hasNext()) {
                   String element = iterator.next();
                   System.out.println(element);
                   // Nu se poate modifica colecția aici dacă alt fir de execuție poate avea acces
               }
           }
           // Modificarea colecției în afara blocului sincronizat sau de către alt fir
           // poate încă provoca `ConcurrentModificationException` în timpul iterării paralele
       }
    }
    
  3. Utilizarea iteratorului pentru eliminarea elementelor: Dacă trebuie să eliminați elemente în timpul iterării, folosiți metoda remove() a iteratorului (dacă este suportată de colecție).

    // Exemplu de eliminare cu 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);
                // Eliminarea elementului prin iterator este sigură
                if (element.equals("B")) {
                    iterator.remove();
                }
            }
            System.out.println("Lista finală: " + list);
        }
    }
    
  4. Iterarea peste o copie a colecției: Creați o copie a colecției și iterați peste ea. Modificările în colecția originală nu vor afecta iterația.

    // Exemplu de iterare peste o copie
    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");
    
            // Creăm o copie pentru iterare
            List<String> copyList = new ArrayList<>(list);
    
            for (String element : copyList) {
                System.out.println(element);
                // Putem modifica colecția originală în timpul iterării peste copie
                if (element.equals("B")) {
                    list.remove(element);
                }
            }
            System.out.println("Lista finală: " + list);
        }
    }
    

Alegerea abordării depinde de cerințele specifice: dacă trebuie să modificați colecția în timpul iterării, cât de importantă este performanța și dacă codul rulează într-un mediu multi-thread. CopyOnWriteArrayList/CopyOnWriteArraySet sunt potrivite pentru citiri frecvente și scrieri rare. Blocarea este potrivită pentru modificări mai rare, dar posibil mai frecvente decât cu CopyOnWrite. Utilizarea Iterator.remove() sau iterarea peste o copie este adecvată atunci când modificările se fac doar în cadrul unei singure iterații sau thread.