Iterator for ConcurrentHashMap giving strange result - java

ConcurrentHashMap is thread safe. So if am adding any values to map at the time of iterating, it should not consider them. Below is my code:
public class Test {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
map.put("ONE", 1);
map.put("TWO", 2);
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
System.out.println(key + " : " + map.get(key));
map.put("9", 10); // This should not be reflected in the Iterator
map.put("5", 10); // This should not be reflected in the Iterator
}
}
}
Output:
TWO : 2
ONE : 1
9 : 10
My question is why iterator considering map.put("9", 10);

ConcurrentHashMap is thread safe. So if am adding any values to map at the time of iterating, it should not consider them.
That is not correct. This is what the javadoc says:
"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."
Note the "or since"!
And it also says that the iterators are "weakly consistent" which means that:
"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."
In short, you are expecting properties of the iterators that the javadocs plainly do not say that they have.

ConcurrentHashMap is thread safe, but this is a different scenario.
With a List you can use ListIterator with ListIterator.Add which is designed for that.
With HashMap I see two solutions:
Convert to List and then back to HashMap
Use an additional HashMap
for new elements, after the loop ends, use another loop to add all new
elements (not quite elegant, but works!).
With Java 8 you may consider using lambda-expressions to simplify the code.

Related

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.

Java HashMap add new entry while iterating

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 */}

Remove an Element From HashTable?

I want to remove elements from HashTable,
I use hashTable.remove() for this but not getting
Hashtable<Integer,String> players = new Hashtable<Integer,String>();
players.put(1, "Sachin Tendulkar");
players.put(2, "Rahul Dravid");
players.put(3, "Virat Kohli");
players.put(4, "Rohit Sharma");
Enumeration<String> enumration = players.elements();
while(enumration.hasMoreElements()){
String elmnt = enumration.nextElement();
System.out.println(elmnt);
if(elmnt.startsWith("R")){
players.remove(elmnt);
System.out.println(elmnt+"::Contains R");
}
}
System.out.println(players);
The output that i get is:
Rohit Sharma
Rohit Sharma::Contains R
Virat Kohli
Rahul Dravid
Rahul Dravid::Contains R
Sachin Tendulkar
{4=Rohit Sharma, 3=Virat Kohli, 2=Rahul Dravid, 1=Sachin Tendulkar}
That's because you're trying to remove the value, but the remove() method expects a key.
When you're calling player.elements() you get an enumeration of the values, not the keys. The remove() method works on a key, not a value.
From HashTable remove() documentation
public V remove(Object key):
Removes the key (and its corresponding value) from this hashtable. This method does nothing if the key is not in the hashtable.
Also, from the same documentation, here's a brief explanation on why it is not a good idea to use Enumeration to remove elements:
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.
What I would suggest you to do is to iterate over the entries, not the values.
for (Entry<Integer, String> entry: players.entrySet()){
String value = entry.getValue();
System.out.println(value);
if(value.startsWith("R")){
players.remove(entry.getKey());
System.out.println(value+"::Contains R");
}
}
This way you have both the key and the value in each iteration. You can examine the value (which is the player's name) and then use the key in order to manipulate your table.
for(Iterator<Map.Entry<Integer, String>> iter = players.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<Integer, String> e = iter.next();
if(e.getValue().startsWith("R")) {
iter.remove();
}
}
You need to pass key as argument in hashTable.remove() to remove from HashTable.
Reference : Remove from HashTable
Best way to remove an entry from a hash table.
This may be helpful to you, have a look at it.
Don't remove from enumerations.
Javadocs:
"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."
You should using iterator:
import java.util.Hashtable;
import java.util.Iterator;
public class Test {
public static void main(String[] args) {
Hashtable<Integer,String> players = new Hashtable<Integer,String>();
players.put(1, "Sachin Tendulkar");
players.put(2, "Rahul Dravid");
players.put(3, "Virat Kohli");
players.put(4, "Rohit Sharma");
Iterator<Integer> iterators = players.keySet().iterator();
while(iterators.hasNext()) {
int key = iterators.next();
if(players.get(key).startsWith("R")) {
iterators.remove();
}
}
System.out.println(players);
}
}
please add key to remove() method rather than value.
Hashtable<String, Integer> h = new Hashtable<String, Integer>();
h.put("n1", 30);
h.put("n2", 30);
h.remove("n1"); //here we are giving "n1" which is key of the hashTable
players.remove(elmnt); method try to remove the entry of elmt key. But you passed value of Hashtable instead of key.
Use players.keySet() to get the keys in place of players.elements().
Here is your syntax for coding for removing an element from hash tables:-
public virtual void Remove(
Object key
)
A mutual hash table can be manipulated with hash ref and hash remove. Concurrently by multiple threads and the operations are protected by a table-specific semaphore as needed.
Whereas, Deleting a record must not hinder later searches. In other words, the search process must still pass through the newly emptied slot to reach records whose probe sequence passed through this slot.

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

How to get a limited number of values from a HashMap or LinkedHashMap?

Say I have a LinkedHashMap containing 216 entries, how would I get the first 100 values (here, of type Object) from a LinkedHashMap<Integer, Object>.
Well to start with, doing this for HashMap as per your title, doesn't make much sense - HashMap has no particular order, and the order may change between calls. It makes more sense for LinkedHashMap though.
There, I'd use Guava's Iterables.limit method:
Iterable<Object> first100Values = Iterables.limit(map.values(), 100);
or
// Or whatever type you're interested in...
Iterable<Map.Entry<Integer, Object>> firstEntries =
Iterables.limit(map.entrySet(), 100);
You can then create a list from that, or iterate over it, or whatever you want to do.
Ugly One-Liner
This ugly one-liner would do (and return a ArrayList<Object> in the question's case):
Collections.list(Collections.enumeration(lhMap.values())).subList(0, 100)
This would work for a HashMap as well, however HashMap being backed by a HashSet there's not guarantee that you will get the first 100 values that were entered; it would work on other types, with similar limitations.
Notes:
relatively unefficient (read the Javadoc to know why - though there's worse!),
careful when using views (read the Javadoc to know more),
I did mention it was ugly.
Step-By-Step Usage Example
(as per the OP's comment)
Map<Integer, Pair<Double, SelectedRoad>> hashmap3 =
new LinkedHashMap<Integer, Pair<Double, SelectedRoad>>();
// [...] add 216 elements to hasmap3 here somehow
ArrayList<Pair<Double,SelectedRoad>> firstPairs =
Collections.list(Collections.enumeration(hashmap3.values())).subList(0, 100)
// you can then view your Pairs' SelectedRow values with them with:
// (assuming that:
// - your Pair class comes from Apache Commons Lang 3.0
// - your SelectedRoad class implements a decent toString() )
for (final Pair<Double, SelectedRoad> p : firstPairs) {
System.out.println("double: " + p.left);
System.out.println("road : " + p.right);
}
You can do:
Map<Integer, Object> records;
List<Entry<Integer, Object>> firstHundredRecords
= new ArrayList<Entry<Integer, Object>>(records.entrySet()).subList(0, 100);
Although note that this will copy all the entries from the map.
To copy only the records you need with using a library.
Map<Integer, Object> records;
List<Entry<Integer, Object>> firstHundredRecords = new ArrayList<>();
for(Entry<Integer, Object> entry : records.entrySet()) {
firstHundredRecords.add(entry);
if (firstHundredRecords.size()>=100) break;
}
You can use counter. Your foreach loop will exit when your counter reached 100.
Write a loop which uses a Iterator.next() 100 times, and then stops.
I was going to say something about NavigableMap and SortedMap - but their interfaces are defined in terms of keys, not indexes. But they may be useful nevertheless, depending on what your actual underlying problem is.

Categories