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();
}
}
}