Set<String> tempSet = new HashSet<String>();
for (Map.Entry<String, String> entry : map.entrySet()) {
String orginalKeys = entry.getKey();
System.out.println(orginalKeys);
String newKey = orginalKeys.replace('.','/');
if (!newKey.equals(orginalKeys)) {
map.put(newKey, entry.getValue());
tempSet.add(orginalKeys);
map.remove(orginalKeys);
}
}
System.out.println(map);
This replaces only one key, but i want to replace all with the above occurrences. What is the wrong thing i am going with?
I see no problem with this code. Assuming, your map has more than one key that matches the critria, e.g. contains at least one dot (.), more than one new keys should have been created with the values taken from the originalkeys and more than one of those originalkeys should have been deleted.
An alternative approach: simply create a new map and drop the old one:
Map<String, String> newMap = new HashMap<String, String>();
for (Map.Entry<String, String> entry : map.entrySet())
newMap.put(entry.getKey().replace('.', '/'), entry.getValue());
map = newMap;
(If you still need that tempSet, modify the code inside the for loop)
This replaces only one key, but i want to replace all with the above occurrences. What is the wrong thing i am going with?
Normally I'd expect your code to give ConcurrentModificationException. The problem is that you are modifying the map while you are iterating it.
The javadoc for HashMap says this:
"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."
"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 fail-fast behavior of iterators should be used only to detect bugs."
If you look at both my solution and #Colin Herbert's solution in the answers to your previous question, you will see that we carefully record what needs to be changed in separate data structures. Only when the iteration is complete do we make the changes to the original Map. This is critical. Without it the solutions don't work.
ideally this code should throw java.util.ConcurrentModificationException as you are iterating through the map.entrySet()) and at the same time updating the map.put(newKey, entry.getValue());
As indicated earlier, you could create a new temporary map and replace with original one
or
you could get the keys in a list and iterate the list
String[] keyArray = map.keySet().toArray(new String[map.size()]);
Set<String> tempSet = new HashSet<String>();
for (String orginalKeys : keyArray) {
System.out.println(orginalKeys);
String newKey = orginalKeys.replace('.', '/');
if (!newKey.equals(orginalKeys)) {
map.put(newKey, map.get(orginalKeys));
tempSet.add(orginalKeys);
map.remove(orginalKeys);
}
}
System.out.println(map);
I'm not sure whether you can modify a map while iterating over it. Try collecting the entries to change in a list first, then later looping over that list to make the changes.
Related
I have below codes and i would expect from case 2 scenario to throw ConcurrentModificationException ,but it runs successfully. As I know If i do the same thing with single key in map it does not throw exception because here
but again when I am reproducing this scenario having multiple keys with two cases -
modification by new key.
modification by existing key.
Case 1:
Map<String,String> mp = new HashMap<String,String>();
mp.put("1", "10");
mp.put("2", "11");
mp.put("3", "12");
mp.put("4", "13");
for (String key :mp.keySet()) {
mp.put("5", "14");
}
This will work as expected , throws ConcurrentModificationException.
Case 2:
Map<String,String> mp = new HashMap<String,String>();
mp.put("1", "10");
mp.put("2", "11");
mp.put("3", "12");
mp.put("4", "13");
for (String key :mp.keySet()) {
mp.put(key, "14");
}
It will not throws ConcurrentModificationException. Why ??
In first case you are modifying the structure of the map so you get CME (adding a new key / value pair). In the second case you are not modifying the structure (overwriting values for existing keys). so you won't get CME
ConcurrentModificationException is thrown when its structure gets changed while iterating over it but in second case there is no change in structure . It means there is just update for existing key in map (neither addition or deletion in second case which would have caused change in structure)
See http://javahungry.blogspot.com/2014/04/fail-fast-iterator-vs-fail-safe-iterator-difference-with-example-in-java.html
You can refer to the javadoc:
The iterators [...] 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.
And the definition of structural modification can be found there too:
A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification
Finally, it is worth also reading the last paragraph:
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 fail-fast behavior of iterators should be used only to detect bugs.
My app will receive a Hashmap<String,Object> from another application.
Is there any way to trim the string keys without iterating the hashmap which leads to performance downgrade becoz Hashmap may contain a lot of real data entries.
Thanks
You have two options:
trim before adding
iterate through MapEntries ad update all keys
In your example you have no choice, and you need to iterate over those keys.
Althought in java 8 you could use pararell streams to boost up such operation. But I would not recommend it in multithread enviroment.
One thing to note beforehand:
The map might contain 2 keys where the first is the trimmed version of the second. By doing what you want, it would overwrite/remove one of them from the map! E.g. the map might contain the keys "a " and "a", and by trimming the keys one of them will disappear!
HashMap does not provide any way to manipulate keys without iterating over them.
You can either "copy" the entries to a new map with keys trimmed (as with #RuchiraGayanRanaweera's solution), or you can do it in the same map like this:
Solution #1: Duplicate entry set and replace the different keys
So what you may do is iterate over the entries, and trim the keys. This also means that if the trimmed key is not equal to the original, you have to remove the entry with the old key and put it again with the new one. You only need to replace the entry if the trimmed version is different:
Map<String, Object> map = new HashMap<>();
for (Entry<String, Object> entry : new HashSet<>(map.entrySet())) {
String trimmed = entry.getKey().trim();
if (!trimmed.equals(entry.getKey())) {
map.remove(entry.getKey());
map.put(trimmed, entry.getValue());
}
}
Note that it is necessary to create a new Set of the entry set because quoting from the javadoc of HashMap.entrySet():
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.
Solution #2: Collect first then replace the different keys
Another option is to collect the keys where the trimmed key is different, and change only those after the first iteration. This solution has the advantage of not having to "duplicate" the entry set to iterate over it. If there are relatively few keys whose trimmed variant is different, probably this is the fastest solution:
Map<String, Object> map = new HashMap<>();
// Set to store the modified keys,
// Also store the trimmed String for performance reasons
Set<String[]> modifiedSet = new HashSet<>();
for (Entry<String, Object> entry : map.entrySet()) {
String trimmed = entry.getKey().trim();
if (!trimmed.equals(entry.getKey()))
modifiedSet.add(new String[]{entry.getKey(), trimmed});
}
// Changing a key can be done in one step:
// Removing the old entry (which returns the old value) and put the new
for (String[] modified : modifiedSet)
map.put(modified[1], map.remove(modified[0]));
If there is no way to trim() keys before adding to HashMap then you have to do something like following:
HashMap<String,Object> map=new HashMap<>();
HashMap<String,Object> newMap=new HashMap<>();
for(Map.Entry<String,Object> entry:map.entrySet()){
newMap.put(entry.getKey().trim(),entry.getValue());
}
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.
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
}
Is following code the safe way to remove a element in the Hashtable?
Enumeration keys = siCache.keys(); //siCache is Hashtable
while(keys.hasMoreElements())
{
String k = (String) keys.nextElement();
Object v = siCache.get(k);
if(condition) siCache.remove(k);
}
Use the Iterator of the entry set, the key set, or the value set, and call Iterator.remove().
Removing an element from a Hashtable while enumerating the keys is potentially risky. Here's what the javadoc says:
"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. The Enumerations returned by Hashtable's keys and elements methods are not fail-fast."
The implication is clear: arbitrary, non-deterministic behaviour is possible if you do that.
Solutions:
If you are using J2SE, use keySet(). Or better still, don't Use Hashtable.
If you are using J2ME, build a list of keys to be removed, and remove them later ... or pray hard :-).
There's a distinct difference between using...
Enumeration keys = siCache.keys();
and using...
Iterator iterator = siCache.entrySet().iterator()
Option 1 will not throw a ConcurrentModificationException when you remove elements in the collection whilst iterating, whereas option 2 will.
As for why... I believe that when you create the keys Enumeration in your example it's a literal copy of the tables key set, which is not kept in sync with modifications to the table itself.
This may or may not be an issue for you. If the table is used concurrently though you may want to switch to using the collections iterators.
It is safe. But what made you think it may not??
tested with the following code.
public static void main(String[] args) {
// TODO Auto-generated method stub
Hashtable siCache = new Hashtable();
siCache.put("key", "value");
siCache.put("key1", "value1");
Enumeration keys = siCache.keys(); //siCache is Hashtable
while(keys.hasMoreElements())
{
String k = (String) keys.nextElement();
Object v = siCache.get(k);
if(true) siCache.remove(k);
}
System.out.println(siCache.size());
}
output : 0