This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 6 years ago.
I have the following code below
Map<String, Integer> buyingItemEnumerationMap = this.toBuyItemEnumeration;
for (Entry<String, Integer> item : buyingItemEnumerationMap.entrySet()) {
if(RandomEngine.boolChance(50)){ //will delete?
buyingItemEnumerationMap.remove(item.getKey());
}
if(buyingItemEnumerationMap.size() == 1){
break;
}
}
now I am working with an android game and the code above is running in multithreaded way. Now I am having an exception which is java.util.ConcurrentModificationException. I already researched on how I can solve the problem but seems not to work on me.
What I am doing on the code above is to remove an entry randomly. How can I implement it there?
You cannot remove an element from a collection while iterating it unless you use an Iterator.
This is what's causing the exception.
buyingItemEnumerationMap.remove(item.getKey());
Use Iterator#remove() to remove an element while iterating over your collection like
Iterator<Map.Entry<String, Integer>> iterator =
buyingItemEnumerationMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> item = iterator.next();
if(RandomEngine.boolChance(50)){ //will delete?
iterator.remove();
}
//..
}
EDIT : (in response to OP's comment)
Yes, the deletions done through Iterator#remove() over the Set returned by HashMap.entrySet() would reflect in the underlying Map as the Set is backed by it. Quoting the JavaDoc here:
Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.
Use Iterator.
Iterator<Map.Entry<String, Integer>> iter = this.toBuyItemEnumeration;
while (iter.hasNext()) {
Map.Entry<String, Integer> entry = iter.next();
if (some-condition){
iter.remove();
}
}
HOW IT WORKS ?
The javadoc for ConcurrentModificationException says:
If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
The field int expectedModCount is initiated to be equal to the field protected transient int modCount = 0; (and this is valid for Collections and Maps), and modCount keeps track of the structural modifications over the object. If modCount at some point of the iteration gets unequal to expectedModCount, then a ConcurrentModificationException is thrown.
With using Iterator to make structural changes over the map/collection (like removing elements), we make sure that the removal operation will be executed properly, e.g. the modCount will be equal to the expectedModCount.
Use iterator in the forEach loop and use iterator.remove();
Yes you can't delete entries in a Map while traversing it by it's own property. different approch.
Related
I have a program that is single-threaded that uses a Map where items are removed one by one while iterating. I have read that iterator can be used here to avoid ConcurrentModificationException but why not use ConcurrentHashMap instead which seems to be much cleaner?
My code:
private final Map<Integer, Row> rowMap;
.....
private void shutDown() {
for (Integer rowNumber : rowMap.keySet()) {
deleteRow(rowNumber)
}
}
....
For my scenario, using a iterator means declaring it final so closeRow() and deleteRow() methods have access to it for removing it. Additionally, the iterator.remove() method does not return the value of the item being removed which is necessary in my case.
My question is, what is the most efficient way to do it so it doesn't throw ConcurrentModificationException? Is it using an iterator or making rowMap a ConcurrentHashMap?
Use ConcurrentHashMap only if it's shared among threads.
In single thread, CurrentModificationException is thrown when the object is modified while an iterator is being used.
There are two ways to remove elements from a collection such as list and map. One is by calling remove on the collection. The other is using an iterator. But they can't be used together. If you remove an element using the remove method of the collection object, it would invalidate the state of the iterator.
List<Integer> list = new ArrayList(List.of(1,2,3,4,5));
Iterator<Integer> it = list.iterator();
list.remove(0);
while(it.hasNext()){
System.out.print(it.next());
}
Here's the exception:
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at Main.main(Main.java:15)
It's a fairly straightforward iterator pattern.
Iterator<Map.Entry<Integer,Row>> it = rowMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer,Row> ent = it.next();
Integer key = ent.getKey();
Row row = ent.getValue(); // before the remove
it.remove();
// ... do what you want with key and row;
}
So, we're iterating through the map with an explicit iterator, which allows us to use the iterator's remove method during iteration. We're iterating over the "entry set" view of the map, which allows us to retrieve both key and value from the single iterator.
Documentation link
public Set<Map.Entry<K,V>> entrySet()
Returns a Set view of the mappings contained in this map. The set is
backed by the map, so changes to the map are reflected in the set, and
vice-versa. If the map is modified while an iteration over the set is
in progress (except through the iterator's own remove operation, or
through the setValue operation on a map entry returned by the
iterator) the results of the iteration are undefined.
I created an iterator() and then removed the 1st entry from the map before iterating it. I always get the 1st item returned from the iterator. But when I remove the 2nd or subsequent entries, the current iterator does not return that entry.
Example of removing 1st entry from map:
Map<Integer,Integer> m1 = new ConcurrentHashMap<>();
m1.put(4, 1);
m1.put(5, 2);
m1.put(6, 3);
Iterator i1 = m1.entrySet().iterator();
m1.remove(4); // remove entry from map
while (i1.hasNext())
System.out.println("value :: "+i1.next()); //still shows entry 4=1
and the output is:
value :: 4=1
value :: 5=2
value :: 6=3
Example of removing 3rd entry from map:
Map<Integer,Integer> m1 = new ConcurrentHashMap<>();
m1.put(4, 1);
m1.put(5, 2);
m1.put(6, 3);
Iterator i1 = m1.entrySet().iterator();
m1.remove(6); // remove entry from map
while (i1.hasNext())
System.out.println("value :: "+i1.next()); //does not show entry 6=3
and the output is:
value :: 4=1
value :: 5=2
Why is removing the 1st entry from the map not reflected in the iterator, but removing the 2nd or subsequent entry is?
The Java documentation says:
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.
That means, its iterators reflect the state of the hash table at the point of creation of the iterator.
And when we add or remove an entry to or from the Map, the Iterator will show the original entries?
According to this iterators and spliterators are weakly consistent. The definition of "weakly consistent" can be found here:
Most concurrent Collection implementations (including most Queues)
also differ from the usual java.util conventions in that their
Iterators and Spliterators provide weakly consistent rather than
fast-fail traversal:
they may proceed concurrently with other operations
they will never throw ConcurrentModificationException
they are guaranteed to traverse
elements as they existed upon construction exactly once, and may (but
are not guaranteed to) reflect any modifications subsequent to
construction.
Which means that any modifications made after an iterator has been created may be reflected, but it's not guaranteed. That's just a normal behaviour of a concurrent iterator\spliterator.
To achieve exactly once iteration behavior, when you remove an element via the Iterator object, the iterator data structure would need to be updated to keep it in step with what has happened to the collection. This is not possible in the current implementations because they don't keep links to the outstanding iterators. And if they did, they would need to use Reference objects or risk memory leaks.
The iterator is guaranteed to reflect the state of the map at the time of it's creation. Further changes may be reflected in the iterator, but they do not have to be.
Think Iterator as a LinkedList and you have head reference with you. If you happen to remove the head from linked list and does not reset the head.next value and start iterating from head you still be traversing the from the same head because you are using an outdated head reference. But when you remove non-head elements the prior element.next is updated.
The answer is in the documentation you quoted:
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.
At OR since. The iterator might or might not show changes to the map since the iterator's creation.
It is not practical for the designers to enforce more precise behavior during concurrent modification and iteration, but it is not broken.
Actual value check is done in next(),
public final Map.Entry<K,V> next() {
Node<K,V> p;
if ((p = next) == null)
throw new NoSuchElementException();
K k = p.key;
V v = p.val;
lastReturned = p;
advance();
return new MapEntry<K,V>(k, v, map);
}
advance method advances if possible, returning next valid node, or null if none.
So for first entry K k = 4; V v = 1; even if it is removed. But for subsequent k,v would decided with update fromadvance()
So if you call next() after remove, it won't be there(which is obvious),
Map<Integer,Integer> m1 = new ConcurrentHashMap<>();
m1.put(4, 1);
m1.put(5, 2);
m1.put(6, 3);
Iterator<Map.Entry<Integer, Integer>> i1 = m1.entrySet().iterator();
m1.remove(4); // remove entry from map
i1.next();
while (i1.hasNext())
System.out.println("value :: "+i1.next());
I have a map
Map<String, String> map = new HashMap<String, String>();
map.put("Pujan", "pujan");
map.put("Swati", "swati");
map.put("Manish", "manish");
map.put("Jayant", "jayant");
Iterator<Map.Entry<String, String>> itr = map.entrySet().iterator();
while(itr.hasNext()){
Entry<String,String> entry=(Entry<String, String>) itr.next();
map.put("Manish", "Updated");
}
I don't get an exception here (where I am trying to modify an existing key value "Manish"). But if I try to add a new key map.put("Manish123", "Updated") I get ConcurrentModificationException.
Because your aren't modifying the iterator,
put will mutate an existing entry in this case because a Map.Entry with the same key already exists in the Map.
If you see the Javadoc for the modCount field of a HashMap (in Java 8 HashMap.java source), you will see:
/**
* 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).
*/
Thus this field keeps the number of times there have been structural modifications to the map. The various iterators in this class throw the ConcurrentModificationException (a better name could have been chosen) when the expected modification count expectedModCount (which is initialized to modCount when you construct this iterator, for example, at the line Iterator<Map.Entry<String, String>> itr = map.entrySet().iterator();) does not match modCount which is mutated any time there are structural modifications to the map (e.g. calling put with a new entry, among other things). Note that different threads are not involved here. All of this can happen in one single thread, for example, when you remove entries from the map or add entries to it while iterating).
As you can now relate, remapping an existing key to a different value should not result in changes to the internal structure of the hash map (since it simply replaces the value associated with the key). And all you are doing is simply remapping the key Manish to a value Updated repeatedly as many times as there are entries in the map (which is 4 and is fixed for the duration of iteration). If, however, added or removed any key you will get the ConcurrentModificationException.
This is analogous to the following code (Note: for illustration purposes only):
List<String> names = Arrays.asList("Larry", "Moe", "Curly");
int i = 0;
Iterator<String> strIter = names.iterator();
while (strIter.hasNext()) {
names.set(i, strIter.next() + " " + i); // value changed, no structural modification to the list
i += 1;
}
System.out.println(names);
which prints:
[Larry 0, Moe 1, Curly 2]
According to Java API : Iterating over collection using Iterator is subject to ConcurrentModificationException if Collection is modified after Iteration started, but this only happens in case of fail-fast Iterators.
There are two types of Iterators in Java, fail-fast and fail-safe, check difference between fail-safe and fail-fast Iterator for more details.
Fail-Fast Iterators in Java
Difference between fail-safe vs fail-fast iterator in javaAs name suggest fail-fast Iterators fail as soon as they realized that structure of Collection has been changed since iteration has begun. Structural changes means adding, removing or updating any element from collection while one thread is Iterating over that collection. fail-fast behavior is implemented by keeping a modification count and if iteration thread realizes the change in modification count it throws ConcurrentModificationException.
Java doc says this is not a guaranteed behavior instead its done of "best effort basis", So application programming can not rely on this behavior. Also since multiple threads are involved while updating and checking modification count and this check is done without synchronization, there is a chance that Iteration thread still sees a stale value and might not be able to detect any change done by parallel threads. Iterators returned by most of JDK1.4 collection are fail-fast including Vector, ArrayList, HashSet etc
Fail-Safe Iterator in java
Contrary to fail-fast Iterator, fail-safe iterator doesn't throw any Exception if Collection is modified structurally
while one thread is Iterating over it because they work on clone of Collection instead of original collection and that’s why they are called as fail-safe iterator. Iterator of CopyOnWriteArrayList is an example of fail-safe Iterator also iterator written by ConcurrentHashMap keySet is also fail-safe iterator and never throw ConcurrentModificationException in Java.
I want to write code like this -
for (Map.Entry<Long, Integer> e : map.entrySet()){
map.remove(k);
map.put(x, value);
}
but I got java.util.ConcurrentModificationException
I tried to use Iterator also but I got the same Exception
Explanation why it caused ConcurrentModificationException
map.remove(k);
map.put(x, value);
for-each loop also internally create a iterator of the entrySet of map. While iterating over map you have modified the structure of the map by putting the value again to the map (map.put(x,value)) which cause this ConcurrentModificationException.
It is even well explained in documentation -
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. Thus, in the face of concurrent
modification, the iterator fails quickly and cleanly, rather than
risking arbitrary, non-deterministic behavior at an undetermined time
in the future.
How to solve this -
you must change the change the structure of this map while iterating, you can insert this values later, like keep a temporary map and add this to the main map once iteration is finished his job.
Map<Long, Integer> tempMap = new HashMap<>();
for (Map.Entry<Long, Integer> e : map.entrySet()){
map.remove(k);
tempMap.put(x, value);
}
map.putAll(tempMap);
Iterate over a copy and you can add/remove just fine:
for (Map.Entry<Long, Integer> e : new LinkedHashMap<Long, Integer>(map).entrySet()){
map.remove(k);
map.put(x, value);
}
It's not even any more lines of code, because the copy ims made in-line via the copy constructor. LinkedHashMap was chosen to preserve iteration order (if that matters).
A sample code snippet for removing an element from the map is given below.
for(Iterator<Map.Entry<Long, Integer>> it = map.entrySet().iterator();it.next();)
{
Map.Entry<String, String> entry = it.next();
if(//some logic)
it.remove();
}
If your code involves a lot of addition and removal , you might just want to use ConcurrentHashMap.ConcurrentHashMap
You will have to create a copy of your map using copy constructor. Now iterate on 1 and modify second map.
I am assuming that you will not need to iterate newly added value as it wont make much sense.
You can achieve your task by creating a copy is because the keys will remain same in both.
EDIT:
I dont think its a good idea to iterate the newly added element to a Hashmap. If you check the api's provided by Iterator then you will find only remove method, there is no add method in it. There is a reason behind this and you can check javadoc for this.
Now coming to the point, on how to iterate newly added element.
Create a copy of your HashMap. So you will iterate one and modify the the other Map.
As the requirement is to both add and remove elements in Map, i would like to use ListIterator for this [this is different from normal Iterator].
I will get the keyset of Map1 and convert it to a list using ArrayList(Collection<? extends E> c).
Now i will get ListIterator from List created in step 3, and add, remove elements in ListIterator as well as in Map2 [Remeber you need to add , remove both in ListIterator and Map2].
Because you can't do that.
An easy solution is to use another temporary map where you put the values you want and finally switch pointers with the original one (i.e Map = newMap )
Try going through the map as follows
while (tree_map.size() > 0){
// if k is key
if (tree_map.containsKey()){
tree_map.remove(k);
}
tree_map.put(x, value);
break;
// can go through the for loop or other code as per requirements
}
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Why is a ConcurrentModificationException thrown and how to debug it
(8 answers)
Closed 3 years ago.
When I execute the following code, I get ConcurrentModificationException
Collection<String> myCollection = Collections.synchronizedList(new ArrayList<String>(10));
myCollection.add("123");
myCollection.add("456");
myCollection.add("789");
for (Iterator it = myCollection.iterator(); it.hasNext();) {
String myObject = (String)it.next();
System.out.println(myObject);
myCollection.remove(myObject);
//it.remove();
}
Why am I getting the exception, even though I am using Collections.synchronizedList?
When I change myCollection to
ConcurrentLinkedQueue<String> myCollection = new ConcurrentLinkedQueue<String>();
I don't get that exception.
How is ConcurrentLinkedQueue in java.util.concurrent different from Collections.synchronizedList ?
A synchronized List will does not provide a new implementation of Iterator. It will use the implementation of the synchronized list. The implementation of iterator() is:
public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
}
From ArrayList:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException
From ConcurrentLinkedQueue#iterator:
Returns an iterator over the elements in this queue in proper sequence. The returned iterator is a "weakly consistent" iterator that will never throw ConcurrentModificationException, and guarantees to traverse elements as they existed upon construction of the iterator, and may (but is not guaranteed to) reflect any modifications subsequent to construction.
The iterators returned by the two collections are different by design.
don't do
myCollection.remove(myObject);
do
it.remove();
There is no need for synchronization or concurrent collection
How is ConcurrentLinkedQueue in java.util.concurrent different from Collections.synchronizedList?
They have different implementations, and therefore may choose whether to throw ConcurrentModificationException, or to handle the situation you describe gracefully. Evidently CLQ handles gracefully, and ArrayList wrapped by Collections.synchronizedList (my guess is the behavior is ArrayList's, not the wrapper's) does not.
As #unbeli says, remove through the iterator, not the collection while iterating.