Java HashMap add new entry while iterating - java

In a HashMap
map = new HashMap<String,String>();
it = map.entrySet().iterator();
while (it.hasNext())
{
entry = it.next();
it.remove(); //safely remove a entry
entry.setValue("new value"); //safely update current value
//how to put new entry set inside this map
//map.put(s1,s2); it throws a concurrent access exception
}
When i trying to add a new entry to map it throws ConcurrentModificationException. For remove and update iterator has safely removing methods. How to add new entry while iterating?

You need to consider what it means to put a value to a Map whilst iterating. HashMap defines no order over which its entries will be iterated over. So when you put a new entry, should the entry be returned by the iterator later or not. Consistency of behaviour is important. However, whichever way you decide you'll get inconsistent behaviour when you put a new value to a preexisting key. If the key has already been iterated over then the change won't appear and will appear if the key has yet to be produced by the iterator.
A simple way to overcome this problem is to create a temporary Map of the new key-value pairs and add the temporary Map to the main Map at the end of your iteration.
Map<String,String> values = ...
Map<String,String> temp = new HashMap<>();
for (Entry<String,String> entry : values.entrySet()) {
if ("some value".equals(entry.getValue()) {
temp.put(entry.getValue(), "another value");
}
}
values.putAll(temp);

You need to use ConcurrentHashMap to add elements while iterating the collection. HashMap uses fail-fast iterator, which throws ConcurrentModificationException when the collection is updated while iterating. Whereas ConcurrentHashMap uses fail-safe iterator, which basically works on the clone of the underlying collection and hence allows modification while iterating.

How about:
map = new HashMap<String,String>();
it = map.entrySet().iterator();
while (it.hasNext())
{
entry = it.next();
entry.setValue("new value"); // update current value
}
I checked the HashMap implementation, it does not change its modification count when updating an entry like this. I also see no reason why this shouldn't be allowed. Nothing is removed, nothing is added and no keys are changed.

What I did was to create an array with the current elements and then iterate through the array:
Feature[] initialFeatures = featureMap.values().toArray(new Feature[featureMap.values().size()]);
for (Feature feature : initialFeatures)
{/* Code that can safely add to the featureMap */}

Related

How can I iterate through a hashset within a hashmap?

I am trying to iterate through a hashmap which contains 8 entries. However one of these entries is a hashset 'balloon' with 2 objects within it. I want to add this to an array list so i can then iterate through it in a for loop/
First part of the code below works, I loop through the hashmap and look for the key I require which is 'balloon'. I need help to add the hashset to an array list.
I am getting a casting error when using Collectors.list and stream
//This is the hashmap I am looping through to find the balloon key
Map<String, Object> types = System.getPartyItems();
for (Map.Entry<String, Object> entry : types.entrySet()) {
if (StringUtils.contains(entry.getKey().toString(), "balloon")) {
//This is where I need to add the balloon hashset to a list to access the entries and values from within.
List<PartyItem> myPartyList = new ArrayList<>();
myPartyList.add (hash set of balloon objects)
Do i need to assign the hash set to a variable before i can set it to the list? Anything I've tried I am getting a casting error eg "class java.util.stream.ReferencePipeline$Head cannot be cast to class java.util.ArrayList"
Any help appreciated.
Thanks
Test if a value is a Set and if it is, add all items to your list.
if (StringUtils.contains(entry.getKey().toString(), "balloon")
&& entry.getValue() instanceof Set) {
myPartyList.addAll((Set)entry.getValue());
}
You can iterate like this:
for(String key: Map.keySet()){
if(StringUtils.contains(key, "balloon")){
Iterator<String> it = hashMap.get("balloon").iterator();
while(it.hasNext()){
// your code here
}
}
}
Instead of iterating through entry just iterate through keys and when you find the balloon get the hashset to iterate through it.
for(String key: types.keySet()){
if(StringUtils.contains(key, "balloon")){
for(Object object: types.get(key)){
//do what you need with object
}
}
}
After your edit it should be like this
for(String key: types.keySet()){
if(StringUtils.contains(key, "balloon")){
ArrayList<Set<PartyItem>> myList = new ArrayList();
myList.add(types.get(key));
}
}
Usually you structure your hashmap as <key, value> and you access your values via their corresponding keys. But they have to match exactly.
In your case your hashmap would look like this:
Map<String, Object> partyItems = myPartyList.getPartyItems();
// or maybe even
Map<String, PartyItem> partyItems = myPartyList.getPartyItems();
And getting the value is as easy as:
Object partyItem = partyItems.get("baloon");
If you are not sure if your paryItems contain a value for your key baloon you can check that first:
if (partyItems.contains("baloon")) {
Object partyItem = partyItems.get("baloon");
}
If you are looking for a part of the key matching baloon:
List<PartyItem> myFilteredPartyItems = partyItems.entrySet().stream()
.filter(e -> e.getKey().contains("baloon"))
.collect(Collectors.toList()))
This is called stream oriented programming (take a look at the Java Stream API), and if your run at least Java 8 you can use those.
And what it does, is turn the entries of the List to a stream, then remove everything which does not contain baloon in the key, and turn the resulting stream, which was not removed back to a list.
Here you also find a very informative tutorial on how to use streams in Java.

Why does this throw Concurrent Modification Exception

The following code works fine when there are more than one modification in a particular map. But when there is only one modification it throws concurrent modification exception
for(Map.Entry<String, List<String>> mapEntry : beanMap.entrySet()) {
for(String dateSet : dateList) {
String mName = mapEntry.getKey();
boolean dateFound = false;
if(beanMap.containsKey(dateSet)) {
dateFound = true;
System.out.println(" Found : "+mapEntry.getKey());
}
if(!dateFound)
{
Map<String, List<String>> modifiedMap = beanMap;
List<String> newBeanList = new ArrayList<String>();
dBean beanData = new Bean(dateSet+"NA","NA","NA",0,0,0);
newBeanList.add(beanData);
System.out.println(" Adding : "+dateSet+" "+"NA");
modifiedMap.put(mName, newBeanList);
}
}
}
In the above code it throws ConcurrentModificationException when modifying the "modifiedMap" only once. May be there is more to it but couldn't find out why.
When you use an enhanced for loop, there is an implicit Iterator working behind the scenes. You attempt to make a copy of beanMap with this line:
Map<String, List<String>> modifiedMap = beanMap;
However, this only creates another reference variable that also refers to the same map object. There is still only one map, and you are modifying it:
modifiedMap.put(mName, newBeanList);
The Iterator then detects that the map is modified when it attempts to iterate to the next entry, resulting in the ConcurrentModificationException.
You can create another Map with new, and put all your modifications into that map while you're iterating the original map.
After you're done iterating the original map, you can call the putAll method on it, passing your new map, to apply all of the modifications you want.
You are not allowed to change the underlying collection while iterating over it using this syntax. The collections are implemented in a fail-fast way, so even a single change will raise the exception.
If you need to change the collection while visiting the elements, use an Iterator.
modifiedMap is the reference to same Map beanMap on which you are iterating. You are modifying the Collection modifiedMap while iteration hence the Exception.

FindBugs warning: Inefficient use of keySet iterator

This a a similar question to [FindBugs warning: Inefficient use of keySet iterator instead of entrySet iterator
However, there I am trying to do something a little different. My current code is here:
for (Double key2 : sortedPolygons.keySet()) {
if (sortedPolygons.get(key2).getExteriorRing().equals(hole)) {
sortedPolygons.remove(key2);
break;
}
}
Doing something like the solution in the link does not work. Here is an implementation of said solution:
for(Map.Entry<Double, Polygon> entry : sortedPolygons.entrySet()) {
if (entry.getValue().getExteriorRing().equals(hole)) {
.....
The problem here is that I am trying to delete the entry. There is no entry.remove(). How can I replace my first block of code, without the FindBugs error:
Inefficient use of keySet iterator instead of entrySet iterator ->
This method accesses the value of a Map entry, using a key that was
retrieved from a keySet iterator. It is more efficient to use an
iterator on the entrySet of the map, to avoid the Map.get(key) lookup.
To note, the underlying structure is TreeMap, and it cannot be changed.
I fail to understand your reasoning: in the first snippet, you use
sortedPolygons.remove(key2);
to remove a key. Nothing prevents you to do the same in the second snippet:
sortedPolygons.remove(entry.getKey());
Whatever the way you iterate, this will lead to a ConcurrentModificationException anyway, because for most collections, you can't modify it while iterating on it, except by using its iterator.
Quote from the javadoc:
The iterators returned by the iterator method of the collections 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.
So the code should be:
for (Iterator<Map.Entry<Double, Polygon>> it = sortedPolygons.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<Double, Polygon> entry = it.next();
if (entry.getValue().getExteriorRing().equals(hole)) {
it.remove();
// if you want to exit the loop as soon as you found a match:
break;
}
}
How about you use the entrySet() iterator as suggested.
for(Iterator<Map.Entry<Double, Ploygon>> iter = sortedPolygons.entrySet().iterator();
iter.hasNext();) {
Map.Entry<Double, Ploygon> entry = iter.next();
if (condition)
iter.remove();
}
However you don't need the key so you can just iterate the values
for(Iterator<Ploygon> iter = sortedPolygons.values().iterator();
iter.hasNext();) {
Ploygon ploygon = iter.next();
if (condition)
iter.remove();
}

Using ConcurrentHashMap efficiently?

I have a Android Application whose core component is a HashMap<String,float[]>.
The System is having high concurrency. e.g
here are the following three situations I have which occur frequently and they are highly overlapping in nature
Iterate through all the keys in the hashmap and do some operation on
its value(read only operations).
Add new key,value pairs in the Hashmap.
Remove Certain keys from the Hashmap.
I do all these operations in different threads and thus am using a ConcurrentHashMap since some inconsistency in retrievals doesnt matter. e.g While iterating the map,if new entries are added then it doesnt matter to not read in those new values immediately as I ensure that next time they are read .
Also while removing the entries I am recreating the iterator everytime to avoid "ConcurrentModificationException"
Suppose , there is a following hashmap(i.e ConcurrentHashmap)
ConcurrentHashMap<String,float[]> test=new ConcurrentHashMap<String, float[]>(200);
Now for Retrieval I do the following
Iterator<String> reader=test.keySet().iterator();
while(reader.hasNext())
{
String s=reader.next();
float[] temp=test.get(s);
//do some operation with float[] temp here(read only operation)
}
and for removal I do the following
boolean temp = true;
while (temp) {
for (String key : test.keySet()) {
temp = false;
if (key.contains("abc")) {
test.remove(key);
temp = true;
break;
}
}
}
and when inserting in new values I simply do
test.put("temp value", new float[10]);
I am not sure if its a very efficient utilisation. Also it does matter not to read in removed values(however I need efficiency ,and since the iterator is again created during the function call,its guaranteed that in the next time I don't get the removed values)so that much inconsistency can be tolerated?
Could someone please tell me an efficient way to do it?
PS. I forgot to mention that why I am doing the removal operation in such a way.
I have now changes the condition on which its deleted from equal to contains(there might be multiple stings having the prefix "abc" followed by different suffixes. so I need to delete all those then.
Iterate through all the keys in the hashmap and do some operation on its value(read only operations).
Don't iterate over the key set to then retrieve the values too - iterate over the entry set directly:
for (Map.Entry<String, float[]> e : map.entrySet() {
String key = e.getKey();
float[] value = e.getValue();
//do something with them
}
This is more efficient in general (even for "normal" HashMaps), but it will also reduce contention in your case (half as many accesses to the map).
Add new key,value pairs in the Hashmap.
Yes it is simply: map.put(s, f);
Remove Certain keys from the Hashmap.
If you need to check that the key contains a given substring then you do need to iterate over the keys like you are doing, although I'm not sure why you have a while+for+break instead of a simple for.
Because of the way you are using the ConcurrentHashMap, you are precisely removing its Concurrent characteristics. Your attempts at (re-)synchronization will work very frequently, but not always.
Have you considered to leave the keys in the HashMap? I'm thinking of something like:
public static final float[] DELETED= new float[0] ;
/* delete */
test.put(key,DELETED);
/* insert */
test.put(key,value);
/* iterate */
for(Map.Entry<String,float[]> e: test.entrySet ) {
if( e.getValue() != DELETED ) {
operateOn(e);
}
}
If keys are too volatile (i.e. after a while you would have too many DELETED items), then you can create a cleanup Thread.
According to ConcurrentHashMap API its iterators never throw ConcurrentModificationException so you dont need to break after removal. But in any case the correct way to iterate and remove is this
for (Iterator<String> i = test.keySet().iterator(); i.hasNext();) {
String next = i.next();
if (next.equals("abc")) {
i.remove();
}
}
this way it will work even with fail-fast iterators without ConcurrentModificationException

Java concurrent modification exception

I have written following code which is resulting in concurrent modification exception. How can I prevent it ? The idea is to escape all values of the Map and reconstruct the object (dO) back with new param map.
try {
Map<String,String[]> paramMap = dO.getParameterMap();
Set<Map.Entry<String, String[]>> entries = paramMap.entrySet();
Iterator<Map.Entry<String, String[]>> it = entries.iterator();
while (it.hasNext()) {
Map.Entry<String, String[]> entry = it.next();
String[] values = entry.getValue();
List<String> valList = new ArrayList<String>();
if (values != null) {
for (String value : values) {
valList.add(escapeHTML(value));
}
dO.removeParameter(entry.getKey());
//Please note that Parameter is a hashMap so , Is it required to remove the entry first before inserting or it will replace the new value associated with key . How it works in Java ?
dO.addParameter(entry.getKey(),valList.toArray(new String[valList.size()]));
}
}
}
the exception is thrown because you are adding/removing things from the map while you are iterating it:
dO.removeParameter(entry.getKey());
dO.addParameter(entry.getKey(),valList.toArray(new String[valList.size()]
you should use iterator.remove() instead.
Not sure you need to alter the keys of Map, it appears all you want to do is alter the values in the arrays.
for(String[] values: dO.getParameterMap().values())
for (int i = 0; i < values.length; i++)
values[i] = escapeHTML(values[i]);
I would make sure the Map does have null values stored. But if you can't change this you will need to add if(values != null)
You should remove/add only if you are changing a key in a map. As I see in the code, you are changing only value. Hence you could use entry.setValue(...) instead.
ConcurrentModificationException is thrown when you use fail-fast iterators (eg: entries.iterator() is fail-fast iterator). What they do is they iterate over original collection object.
To be able to modify and iterate over a collection object you can use fail-safe iterator (eg: List<Book> books = new CopyOnWriteArrayList<>()) This will take a copy inside memory while iterating over the elements and even you will be able to modify it.

Categories