Sobes.tech
Middle

Hoe voorkom je een ConcurrentModificationException bij het doorlopen van een collectie?

sobes.tech AI

Antwoord van AI

Om te voorkomen dat er een ConcurrentModificationException optreedt tijdens het itereren over een collectie, kunnen de volgende benaderingen worden gebruikt:

  1. Gebruik thread-veilige collecties: java.util.concurrent biedt thread-veilige alternatieven voor standaardcollecties. Bijvoorbeeld:

    • CopyOnWriteArrayList in plaats van ArrayList
    • CopyOnWriteArraySet in plaats van HashSet
    • ConcurrentHashMap in plaats van HashMap
    // Voorbeeld met 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 werpt geen ConcurrentModificationException uit
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String element = iterator.next();
                System.out.println(element);
                // Het veilig om de collectie te wijzigen tijdens iteratie
                if (element.equals("B")) {
                    list.remove(element);
                }
            }
            System.out.println("Eindlijst: " + list);
        }
    }
    
  2. Vergrendel de collectie: Synchroniseer toegang tot de collectie met synchronized of java.util.concurrent.locks.

    // Voorbeeld met gesynchroniseerde blok
    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) { // Vergrendel de collectie
               Iterator<String> iterator = list.iterator();
               while (iterator.hasNext()) {
                   String element = iterator.next();
                   System.out.println(element);
                   // Het is niet toegestaan om de collectie hier te wijzigen als een andere thread toegang kan krijgen
               }
           }
           // Het wijzigen van de collectie buiten de gesynchroniseerde blok of door een andere thread
           // kan nog steeds een ConcurrentModificationException veroorzaken tijdens parallelle iteratie
       }
    }
    
  3. Gebruik iterator voor het verwijderen van elementen: Als je elementen wilt verwijderen tijdens het itereren, gebruik dan de remove()-methode van de iterator (indien ondersteund door de collectie).

    // Voorbeeld van verwijderen met 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);
                // Veilig element verwijderen via iterator
                if (element.equals("B")) {
                    iterator.remove();
                }
            }
            System.out.println("Eindlijst: " + list);
        }
    }
    
  4. Itereer over een kopie van de collectie: Maak een kopie van de collectie en doorloop deze. Wijzigingen in de originele collectie beïnvloeden de kopie niet.

    // Voorbeeld van iteratie over een kopie
    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");
    
            // Maak een kopie voor iteratie
            List<String> copyList = new ArrayList<>(list);
    
            for (String element : copyList) {
                System.out.println(element);
                // Je kunt de originele collectie wijzigen tijdens het itereren over de kopie
                if (element.equals("B")) {
                    list.remove(element);
                }
            }
            System.out.println("Eindlijst: " + list);
        }
    }
    

De keuze van aanpak hangt af van de specifieke vereisten: of je de collectie tijdens het itereren moet wijzigen, hoe belangrijk de prestaties zijn en of de code in een multithread-omgeving draait. CopyOnWriteArrayList/CopyOnWriteArraySet zijn geschikt wanneer er veel gelezen wordt en weinig geschreven. Vergrendeling is geschikt wanneer wijzigingen zeldzamer zijn, maar mogelijk frequenter dan bij gebruik van CopyOnWrite. Gebruik van Iterator.remove() of iteratie over een kopie is geschikt wanneer wijzigingen alleen binnen één iteratie of thread plaatsvinden.