ConcurrentModificationException while making a copy of a Map

183 Views Asked by At

I have a JPA Entity class, that has a Map as attribute

@Data
@Builder
@Entity
public class Item {

    @ElementCollection(fetch = FetchType.EAGER)  // we will need those elements in every case
    @CollectionTable(
            name = "item_prop_mapping",
            joinColumns = {
                    @JoinColumn(name = "item_id", referencedColumnName = "id")
            })
    @MapKeyColumn(name = "prop_name")
    @Column(name = "prop_value")
    private Map<String, String> props;
}

As this Map may be changed, I make a copy on another place, before using it for calculation purposes:

var copy = new HashMap<>(item.getProps());

However, I get a ConcurrentModificationException while creating the copy.

Can I use Map.copyOf or will that also fail, as it has a for loop within its code

    @SafeVarargs
    @SuppressWarnings("varargs")
    static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
        if (entries.length == 0) { // implicit null check of entries array
            return ImmutableCollections.emptyMap();
        } else if (entries.length == 1) {
            // implicit null check of the array slot
            return new ImmutableCollections.Map1<>(entries[0].getKey(),
                    entries[0].getValue());
        } else {
            Object[] kva = new Object[entries.length << 1];
            int a = 0;
            for (Entry<? extends K, ? extends V> entry : entries) {
                // implicit null checks of each array slot
                kva[a++] = entry.getKey();
                kva[a++] = entry.getValue();
            }
            return new ImmutableCollections.MapN<>(kva);
        }
    }

Is there any other / better way to do it?


StackTrace is the following:


 "name": "java.util.ConcurrentModificationException",
 "extendedStackTrace": [
        {
          "version": "?",
          "class": "java.util.HashMap$HashIterator",
          "method": "nextNode",
          "line": -1,
          "exact": false,
          "location": "?"
        },
        {
          "class": "java.util.HashMap$EntryIterator",
          "method": "next",
          "line": -1,
          "exact": false,
          "location": "?",
          "version": "?"
        },
        {
          "method": "next",
          "line": -1,
          "exact": false,
          "location": "?",
          "version": "?",
          "class": "java.util.HashMap$EntryIterator"
        },
        {
          "location": "hibernate-core-5.4.6.Final.jar!/",
          "version": "5.4.6.Final",
          "class": "org.hibernate.collection.internal.PersistentMap$EntryIteratorProxy",
          "method": "next",
          "file": "PersistentMap.java",
          "line": 431,
          "exact": false
        },
        {
          "class": "java.util.HashMap",
          "method": "putMapEntries",
          "line": -1,
          "exact": false,
          "location": "?",
          "version": "?"
        },
        {
          "location": "?",
          "version": "?",
          "class": "java.util.HashMap",
          "method": "<init>",
          "line": -1,
          "exact": false
        },

Here is a Test, which will generate the CME

public class CMETest {

    private HashMap<String, Integer> map = new HashMap<>();
    private ExecutorService executorService = Executors.newFixedThreadPool(11);
    private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
    private boolean shutdown;
    private boolean add = true;

    private CyclicBarrier barrier = new CyclicBarrier(12);

    @Test
    void name() throws BrokenBarrierException, InterruptedException {
        for (int i = 0; i < 10; i++) {
            executorService.submit(this::changeMethod);
        }
        executorService.submit(this::copy);
        scheduledExecutorService.schedule(this::stop, 30, TimeUnit.SECONDS);
        barrier.await();
    }

    private void stop() {
        System.out.println("Starting shutdown");
        shutdown = true;
    }

    private void changeMethod() {
        while (!shutdown) {
            try {
                map.put(UUID.randomUUID().toString(), new Random().nextInt(9999));
            } catch (ConcurrentModificationException cme) {
                System.err.println("ConcurrentModificationException");
                fail();
            } catch (Exception ex) {
                System.err.println("Exception");
            }
        }
        System.out.println("Shutdown");
        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    private void copy() {
        while (!shutdown) {
            try {
                var copiedMap = new HashMap<>(map);
                System.out.println("map has " + copiedMap.size() + " entries");
            } catch (ConcurrentModificationException cme) {
                System.err.println("ConcurrentModificationException");
                fail();
            } catch (Exception ex) {
                System.err.println("Exception");
            }
        }
        System.out.println("Shutdown");
        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}
0

There are 0 best solutions below