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.
Related
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);
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();
}
}
I am studying hashmap, I can to know that when a map is iterated. hashmap internally keep a track of the hashmap size and if there is any change in the size hashmap throws ConcurrentModificationException.
Going forward with the same perspective.
Look at this piece of code
HashMap<String, String> myMap = new HashMap<>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
myMap.put("4", "4");
System.out.println("HashMap before iterator size before : " +myMap.size());
try {
for(String key : myMap.keySet()){
myMap.remove("3");
myMap.remove("4");
myMap.put("5", "new8");
myMap.put("6", "new9");
}
} catch(ConcurrentModificationException exception) {
System.out.println("HashMap before iterator size after : " +myMap.size());
}
System.out.println("HashMap before iterator fourth : "+myMap);
The output of the same is :
HashMap before iterator size before 4
HashMap before iterator size after 4
HashMap before iterator fourth : {1=1, 2=2, 5=new8, 6=new9}
Why does the hashmap throw error in these case?
hashmap internally keep a track of the hashmap size and if there is any change in the size hashmap throws ConcurrentModificationException
That's incorrect. For the decision whether to throw ConcurrentModificationException, HashMap keeps track of the number of modifications, not the size of the Map. Adding two entries and removing two entries increments the modification count by 4. Hence the exception.
The Javadoc states:
The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.
Any put or remove operations that actually add or remove an entry cause a structural modification to the Map, so your iterator over myMap.keySet() will throw ConcurrentModificationException.
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
I have a HashMap. I loop through the map like this:
Map<Long, Integer> map = new HashMap<Long, Integer>();
for (Long key : map.keySet() ) {
int value = map.get(key);
value--;
map.put(key, value);
}
Is the way I'm using to update the map safe? Safe in the sense that it doesn't damage the map because of the iteration.
You could consider writing your code more efficiently as:
Map<Long, Integer> map = new HashMap<Long, Integer>();
for (Entry<Long, Integer> entry : map.entrySet() ) {
entry.setValue(entry.getValue() - 1);
}
This is a micro-optimization, but sometimes it matters, and you don't lose anything. It's shorter and clears up any ambiguity about the safety to boot!
As you can see in the HashMap source code, the put method only modifies the modCount when a new key is provided. modCount is used by the iterator to check for changes, and if such a change occurred between two calls to an iterator's next(), a ConcurrentModificationException would be thrown. This means that the way you are using put is safe.
It is perfectly safe operation that you're doing since you're just changing value of an existing key in the Map.
However if you are ever going to delete an entry from Map then remeber to use Iterator.