ConcurrentModificationException - checkForComodification(ArrayList.java) - java

For the Below java program with Hash Map, ConcurrentModification Exception thrown, i had marked the lines where the Exception is thrown in the Program.
I had skipped the login of Insertion of Data into the HashMap for now
import java.util.ArrayList;
import java.util.HashMap;
public class MainClass {
public static void main(String[] args) {
ArrayList<HashMap<String, ArrayList<String>>> arrMain = new ArrayList<HashMap<String, ArrayList<String>>>();
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
ArrayList<String> strings = new ArrayList<String>();
// Code to build the above Maps with all required Data, Skipped for Now
//******************Scenario 1****************
for (HashMap<String, ArrayList<String>> dataMap : arrMain) { //ConcurrentModification Exception
for (String s : dataMap.get("Key")) {
ArrayList<String> newStrings = new ArrayList<String>();
newStrings.addAll(dataMap.get("Key"));
newStrings.add("New String");
dataMap.put("Key", newStrings);
}
}
//******************Scenario 2****************
for (HashMap<String, ArrayList<String>> dataMap : arrMain) {//ConcurrentModification Exception
for (String s : dataMap.get("Key")) {
dataMap.get("Key").add("New String");
}
}
}
}
Error :
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)

When ever you try to modify the Collection while iterate you will get ConcurrentModificationException. You can try with Iterator to avoid this.
Eg: Iterator with Map
Map<String, String> map = new HashMap<>();
map.put("a", "a1");
map.put("b", "b1");
map.put("c", "c1");
System.out.println(map);
Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry) it.next();
System.out.println(pairs.getKey() + " = " + pairs.getValue());
it.remove(); // no a ConcurrentModificationException
}
System.out.println(map);
Out put:
{b=b1, c=c1, a=a1}
b = b1
c = c1
a = a1
{}

You have marked 2 ConcurrentModificationException points in your code but only one is reproducible and reasonable.
The 2nd one:
for (HashMap<String, ArrayList<String>> dataMap : arrMain) {
for (String s : dataMap.get("Key")) { // HERE
dataMap.get("Key").add("New String");
}
}
dataMap.get("Key") returns an ArrayList to which inside the for loop you add another element:
dataMap.get("Key").add("New String");
And right after adding an element (modifying it) the for loop would continue. You add an element to the list right in the middle of iterating over it. This causes the ConcurrentModificationException (under the hood the enhanced for uses the iterator of the list to go over its elements, and the iterators next() method throws this exception if the list is modified since the creation of the iterator).
The 1st point in your code where you indicated ConcurrentModificationException, it causes no exception because you iterate over a list and you modify another list, a new list which you create inside the for loop. But I doubt this is what you really want to do.

You can use ConcurrentHashMap
A hash table supporting full concurrency of retrievals and adjustable expected concurrency for updates.
This class obeys the same functional specification as Hashtable, and includes versions of methods corresponding to each method of Hashtable.
However, even though all operations are thread-safe, retrieval operations do not entail locking, and there is not any support for locking the entire table in a way that prevents all access.
This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.
You can use iterator. iterators are designed to be used by only one thread at a time. They do not throw ConcurrentModificationException.
ConcurrentModificationException
public class ConcurrentModificationException
extends RuntimeException
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.
See also
Concurrent Hash Map article

Related

Confused on the difference between ConcurrentHashMap and HashMap behavior in this example

I'm trying to understand how ConcurrentHashMap works. I found an example, but I'm not able to understand it. Here's its code:
Map<String, Object> myData = new HashMap<String, Object>();
myData.put("A", 1);
myData.put("B", 2);
for (String key : myData.keySet()) {
myData.remove(key);
}
This will throw an exception ConcurrentModificationException at runtime.
However, this code using ConcurrentHashMap will work correctly:
Map<String, Object> myData = new ConcurrentHashMap<String, Object>();
myData.put("A", 1);
myData.put("B", 2);
for (String key : myData.keySet()) }
myData.remove(key);
}
Can someone explain to me why ConcurrentHashMap allows to remove keys while the HashMap throws an exception? Thanks
That's just one of the features of ConcurrentHashMap. To quote from the docs:
Similarly, Iterators, Spliterators and Enumerations return elements
reflecting the state of the hash table at some point at or since the
creation of the iterator/enumeration. They do not throw
ConcurrentModificationException.
ConcurrentHashMap does not really do this to support your use case, however. It's done to allow iteration in one thread happen concurrently with modifications made in other threads.
If this is your only reason for using ConcurrentHashMap, then you should probably reconsider, because it is a lot more expensive than HashMap. You are better off just making a copy of the key set before using it like this:
Map<String, Object> myData = new HashMap<String, Object>();
myData.put("A", 1);
myData.put("B", 2);
for(String key: myData.keySet().toArray(new String[0]))
myData.remove(key);

ConcurrentModificationException when using iterator to remove entry

I have a simple piece of code that loops through a map, checks a condition for each entry, and executes a method on the entry if that condition is true. After that the entry is removed from the map.
To delete an entry from the map I use an Iterator to avoid ConcurrentModificationException's.
Except my code does throw an exception, at the it.remove() line:
Caused by: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.remove(Unknown Source) ~[?:1.8.0_161]
at package.Class.method(Class.java:34) ~[Class.class:?]
After a long search I can't find a way to fix this, all answers suggest using the Iterator.remove() method, but I'm already using it. The documentation for Map.entrySet() clearly specifies that it is possible to remove elements from the set using the Iterator.remove() method.
Any help would be greatly appreciated.
My code:
Iterator<Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<K, V> en = it.next();
if (en.getValue().shouldRun()) {
EventQueue.invokeLater(()->updateSomeGui(en.getKey())); //the map is in no way modified in this method
en.getValue().run();
it.remove(); //line 34
}
}
If you cannot change HashMap to ConcurrentHashMap you can use another approach to your code.
You can create a list of entries containing the entries that you want to delete and then iterate over them and remove them from the original map.
e.g.
HashMap<String, String> map = new HashMap<>();
map.put("1", "a1");
map.put("2", "a2");
map.put("3", "a3");
map.put("4", "a4");
map.put("5", "a5");
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
List<Map.Entry<String, String>> entries = new ArrayList<>();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
if (next.getKey().equals("2")) {
/* instead of remove
iterator.remove();
*/
entries.add(next);
}
}
for (Map.Entry<String, String> entry: entries) {
map.remove(entry.getKey());
}
Please use ConcurrentHashMap in place of HashMap as you are acting on the object in multiple threads. HashMap class isn't thread safe and also doesn't allow such operation. Please refer below link for more information related to this.
https://www.google.co.in/amp/s/www.geeksforgeeks.org/difference-hashmap-concurrenthashmap/amp/
Let me know for more information.
For such purposes you should use the collection views a map exposes:
keySet() lets you iterate over keys. That won't help you, as keys are
usually immutable.
values() is what you need if you just want to access the map values.
If they are mutable objects, you can change directly, no need to put
them back into the map.
entrySet() the most powerful version, lets you change an entry's value
directly.
Example: convert the values of all keys that contain an upperscore to uppercase
for(Map.Entry<String, String> entry:map.entrySet()){
if(entry.getKey().contains("_"))
entry.setValue(entry.getValue().toUpperCase());
}
Actually, if you just want to edit the value objects, do it using the values collection. I assume your map is of type <String, Object>:
for(Object o: map.values()){
if(o instanceof MyBean){
((Mybean)o).doStuff();
}
}

Add elements contained in list and map them to a string

Input
SomeName SomeFine
OtherName OtherFine
SomeOtherName SomeOtherFine
OtherName SomeOtherFine
SomeName OtherFine
Explanation
I want to make a List<Map<String, Integer>> so as to create a list of names and the total fines imposed upon them
Expected output
The output I'm expecting (with reference to the example above) is something like this:
[SomeName=SomeFine+OtherFine, OtherName=OtherFine+SomeOtherFine, SomeOtherName=SomeOtherFine]
Code
I tried using the following code, but it's giving me a ConcurrentModificationException. Here's the code:
public List<Map<String, Integer>> calculateTotalFine(){
List<Map<String, Integer>> myMapList = new ArrayList<Map<String, Integer>>();
ListIterator<CrimeInfo> crimeIterator = list.listIterator();
while(crimeIterator.hasNext()){
String key = crimeIterator.next().getName();
Integer value = crimeIterator.next().getFine();
if(myMapList.isEmpty()){
Map<String, Integer> aMap = new HashMap<String, Integer>();
aMap.put(key, value);
myMapList.add(aMap);
}
else{
Iterator<Map<String, Integer>> mapIterator = myMapList.iterator();
while(mapIterator.hasNext()){
if(mapIterator.next().containsKey(key)){ //<-- Line no. 29
Integer newFine = mapIterator.next().get(key) + value;
mapIterator.remove();
Map<String, Integer> nMap = new HashMap<String, Integer>();
nMap.put(key, newFine);
myMapList.add(nMap);
}
else{
Map<String, Integer> newMap = new HashMap<String, Integer>();
newMap.put(key, value);
myMapList.add(newMap);
}
}
}
}
return myMapList;
}
Actual output
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.company.CoreLogic.calculateTotalFine(CoreLogic.java:29)
Can someone tell me where I'm going wrong?
The problem is that you're iterating over myMapList, but modifying it while you're iterating:
myMapList.add(newMap);
I still haven't quite got to grips with what you're trying to do, but fundamentally you shouldn't be adding to the collection while you're iterating over it. One common approach is to create a new collection which you modify while you're iterating, and then (if necessary) perform a bulk modification of the original collection afterwards.
(As Titus says, you're also calling next() twice within your loop... you need to take more care over how you use your iterators, and use an enhanced-for loop where possible.)

while iterating hashmap i am not getting ConcurrentModification Exception

value pairs into Hashmap.while iterating that map i am inserting 1 more key value pair at the time i am not getting the Exception. Can any one Help me when Concurrent Modification Exception will come.
Mycode:
public static void main(String[] args) {
Map<String, String> hMap = new HashMap<String, String>();
hMap.put("FIRST", "1");
hMap.put("SECOND", "2");
hMap.put("THIRD", "3");
hMap.put("FOURTH", "4");
hMap.put("FIFTH", "5");
hMap.put("SIXTH", "6");
System.out.println("HashMap before iteration : " + hMap);
Iterator<String> hashMapIterator = hMap.keySet().iterator();
while (hashMapIterator.hasNext()) {
String key = hashMapIterator.next();
if (key.equals("FOURTH")) {
System.out.println("key = " + key);
hMap.put("sfsfsfsf2", "dsgfdsg");
}
}
System.out.println("HashMap after iteration : " + hMap);
}
You may get a ConcurentModificationException but there is no garantee. The Map (and HashMap) contract for the keyset is :
If the map is modified while an iteration over the set is in progress
(except through the iterator's own remove operation), the results of
the iteration are undefined.
Some implementations try to fail fast, but again there is no garantee. The Hashmap javadoc even says :
Note that the fail-fast behavior of an iterator cannot be guaranteed
as it is, generally speaking, impossible to make any hard guarantees
in the presence of unsynchronized concurrent modification. Fail-fast
iterators throw ConcurrentModificationException on a best-effort
basis. Therefore, it would be wrong to write a program that depended
on this exception for its correctness
The ConcurrentModificationException occurs because both the Iterator and Map#put can modify the Map at the same time. To circumvent that, create a temporary storage for your #put operations:
Map<String, String> tmp = new HashMap<String, String>();
//...
Iterator<String> iter = hMap.keySet().iterator();
while(iter.hasNext()) {
//...
tmp.put("asdfasdf", "aasdfasdfas");
}
for (Entry<String, String> ops : tmp.entrySet())
hMap.put(ops.getKey(), ops.getValue());
A simple observation: If #put adds a new key while iterating, how should the iterator know that it must possibly "jump back" in its new iteration sequence to get to the newly added key as well?
Other Maps
My guess is that the ConcurrentModificationException might not occur if the key from #put is added to the entrySet such that its position (in the Iterator) is after the currently iterated element, so that the "old" Iterator is still consistent with the "new" entrySet because it has delivered the elements in the same order a "new" Iterator would have. But that's just my guess as to why it may not occur. Coding like this is really bad though, since existing code would depend on the implementation details of hashCode of the key-Class.
HashMap
The implementation of HashMap keeps track of structural modifications to the Map:
The number of times this HashMap has been structurally modified Structural modifications are those that change the number of mappings in the HashMap or otherwise modify its internal structure (e.g., rehash). This field is used to make iterators on Collection-views of the HashMap fail-fast. (See ConcurrentModificationException).
And when an HashIterator is requested to advance it checks if the modCount has changed since its creation. If it has, it throws a ConcurrentModificationException. This means it won't be thrown if you only change a value associated to a key, but don't change the keys in any way.

How safe is to delete already removed ConcurrentHashMap element?

In case of following code, run by multiple threads:
private static final Map<String, keyinfo> mapKeys = new ConcurrentHashMap<String, keyinfo>();
private static void purgeOldKeys() {
for (Map.Entry<String, keyinfo> key : mapKeys.entrySet()) {
if(key.getValue().createTime + keyCacheTime < getCurrentDBTime())
mapKeys.remove(key);
}
}
Can I avoid the synchronizer?
Or because removing already removed element, is not defined according to JavaDoc, the synchronizer will be still required?
In general, when removing from a collection, it is much cleaner (and faster!) to use a full Iterator API instead of the lazy "foreach" notion.
The iterator.remove(); will not invalidate the iterator; and it knows the position it was at. Use this pattern:
for (Iterator<> iter = map.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<> entry = iter.next();
if (testRemoval(entry))
iter.remove(); // <----- remove using the iterator position!
}
It's faster because it does not involve searching the object again. It's more robust, because the iterator knows the object has been removed. In many collections, the code you showed above will "fast fail" because of a concurrent modification.
1) This code can't remove anything because there's a bug in it - mapKeys.remove(key); - key in your code is in fact a Map.Entry. It should be
for (Map.Entry<String, keyinfo> e : map.entrySet()) {
if (e.getValue().createTime + keyCacheTime < getCurrentDBTime())
map.remove(e.getKey());
}
}
2) As for removing entries while iterating over ConcurrentHashMap it is safe, ConcurrentHashMap.entrySet API
The view's iterator is a "weakly consistent" iterator that will never throw ConcurrentModificationException
and this test confirms it
Map<String, String> map = new ConcurrentHashMap<String, String>();
map.put("1", "2");
map.put("2", "2");
map.put("3", "3");
for (String k : map.keySet()) {
map.remove(k);
}
System.out.println(map);
prints
{}

Categories

Resources