Using ConcurrentHashMap efficiently? - java

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

Related

Iterate over key-range of HashMap

Is it possible to iterate over a certain range of keys from a HashMap?
My HashMap contains key-value pairs where the key denotes a certainr row-column in Excel (e.g. "BM" or "AT") and the value is the value in this cell.
For example, my table import is:
startH = {
BQ=2019-11-04,
BU=2019-12-02,
BZ=2020-01-06,
CD=2020-02-03,
CH=2020-03-02,
CM=2020-04-06
}
endH = {
BT=2019-11-25,
BY=2019-12-30,
CC=2020-01-27,
CG=2020-02-24,
CL=2020-03-30,
CP=2020-04-27
}
I need to iterate over those two hashmap using a key-range in order to extract the data in the correct order. For example from "BQ" to "BT".
Explanation
Is it possible to iterate over hashmap but using its index?
No.
A HashMap has no indices. Depending on the underlying implementation it would also be impossible. Java HashMaps are not necessarily represented by a hashing-table. It can switch over to a red-black tree and they do not provide direct access at all. So no, not possible.
There is another fundamental flaw in this approach. HashMap does not maintain any order. Iterating it yields random orders that can change each time you start the program. But for this approach you would need insertion order. Fortunately LinkedHashMap does this. It still does not provide index-based access though.
Solutions
Generation
But, you actually do not even want index based access. You want to retrieve a certain key-range, for example from "BA" to "BM". A good approach that works with HashMap would be to generate your key-range and simply using Map#get to retrieve the data:
char row = 'B';
char columnStart = 'A';
char columnEnd = 'M';
for (char column = columnStart; columnStart <= columnEnd; column++) {
String key = Chararcter.toString(row) + column;
String data = map.get(key);
...
}
You might need to fine-tune it a bit if you need proper edge case handling, like wrapping around the alphabet (use 'A' + (column % alphabetSize)) and maybe it needs some char to int casting and vice versa for the additions, did not test it.
NavigableMap
There is actually a variant of map that offers pretty much what you want out of the box. But at higher cost of performance, compared to a simple HashMap. The interface is called NavigableMap. The class TreeMap is a good implementation. The problem is that it requires an explicit order. The good thing though is that you actually want Strings natural order, which is lexicographical.
So you can simply use it with your existing data and then use the method NavigableMap#subMap:
NavigableMap<String, String> map = new TreeMap<>(...);
String startKey = "BA";
String endKey = "BM";
Map<String, String> subMap = map.subMap(startKey, endKey);
for (Entry<String, String> entry : subMap.entrySet()) {
...
}
If you have to do those kind of requests more than once, this will definitely pay off and it is the perfect data-structure for this use-case.
Linked iteration
As explained before, it is also possible (although not as efficient) to instead have a LinkedHashMap (to maintain insertion order) and then simply iterate over the key range. This has some major drawbacks though, for example it first needs to locate the start of the range by fully iterating to there. And it relies on the fact that you inserted them correctly.
LinkedHashMap<String, String> map = ...
String startKey = "BA";
String endKey = "BM";
boolean isInRange = false;
for (Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
if (!isInRange) {
if (key.equals(startKey)) {
isInRange = true;
} else {
continue;
}
}
...
if (key.equals(endKey)) {
break;
}
}
// rangeLower and rangeUpper can be arguments
int i = 0;
for (Object mapKey : map.keySet()) {
if (i < rangeLower || i > rangeUpper) {
i++;
continue;
}
// Do something with mapKey
}
The above code iterates by getting keyset and explicitly maintaining index and incrementing it in each loop. Another option is to use LinkedHashMap, which maintains a doubly linked list for maintaining insertion order.
I don't believe you can. The algorithm you propose assumes that the keys of a HashMap are ordered and they are not. Order of keys is not guaranteed, only the associations themselves are guaranteed.
You might be able to change the structure of your data to something like this:
ranges = {
BQ=BT,
BU=BY,
....
}
Then the iteration over the HashMap keys (start cells) would easily find the matching end cells.

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

Reset all values in hashmap without iterating?

I am trying reset all values in a HashMap to some default value if a condition fails.
Currently i am doing this by iterating over all the keys and individually resetting the values.Is there any possible way to set a same value to all the keys without iterating?
Something like:
hm.putAll("some val") //hm is hashmap object
You can't avoid iterating but if you're using java-8, you could use the replaceAll method which will do that for you.
Apply the specified function to each entry in this map, replacing each
entry's value with the result of calling the function's Function#map
method with the current entry's key and value.
m.replaceAll((k,v) -> yourDefaultValue);
Basically it iterates through each node of the table the map holds and affect the return value of the function for each value.
#Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Node<K,V>[] tab;
if (function == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
e.value = function.apply(e.key, e.value); //<-- here
}
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
Example:
public static void main (String[] args){
Map<String, Integer> m = new HashMap<>();
m.put("1",1);
m.put("2",2);
System.out.println(m);
m.replaceAll((k,v) -> null);
System.out.println(m);
}
Output:
{1=1, 2=2}
{1=null, 2=null}
You can't avoid iterating in some fashion.
You could get the values via Map.values() and iterate over those. You'll bypass the lookup by key and it's probably the most efficient solution (although I suspect generally that would save you relatively little, and perhaps it's not the most obvious to a casual reader of your code)
IMHO You must create your own Data Structure that extends from Map. Then you can write your method resetAll() and give the default value. A Map is a quick balanced tree that allows you to walk quick in the structure and set the value. No worries about the speed, because the tree will have the same structure before and after the reset.
Only, be carefull with concurrent threads. Maybe you should use ConcurrentHashMap.
public class MyMap<K,V> extends ConcurrentHashMap<K, V>{
public void resetAll(V value){
Iterator<Entry<K, V>> it = this.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
pairs.setValue( value );
}
}
}
Regards
If you're willing to make a copy of it ( a hasmap with default values )
You can first clear your hashmap and then move over the default values
hm.keySet().removeAll();
hm.putAll(defaultMap);
It is not possible to apply an operation to all values in a collection in less than O(n) time, however if your objection is truly with iteration itself, there are some possible alternatives, notably functional programming.
This is made most easy by the Guava library (or natively in Java 8), and their functional programming utilities. Their Maps.transformValues() provides a view of the map, with the provided function applied. This means that the function returns in O(1) time, unlike your iteration, but that the computation is done on the fly whenever you .get() from the returned map. This is obviously a tradeoff - if you only need to .get() certain elements from the transformed map, you save time by avoiding computing unnecessary values. On the other hand, if you know you'll later hit every element at least once, using this behavior means you'll actually waste time. In essence, this approach is O(k) where k is the number of lookups you plan to do. If k is always less than n, then using the transformation approach is optimal.
Read carefully however the caveat at the top of the page; iteration is a simple, easy, and generally ideally efficient way to work with the members of a map. You should only try to optimize past that when absolutely necessary.
Assuming that your problem is not with doing the iteration yourself, but with the fact that O(n) is going on at some point, I would suggest a couple of alternative approaches. Bear in mind I have no idea what you are using this for, so it might not make any sense to you.
Case A: If your set of keys is known and fixed beforehand, keep a copy (not a reference, an actual clone) somewhere with the values reset to the one you want. Then on that condition you mention, simply switch the references to use the default one.
Case B: If they keys change over time, use the idea from case A but add new entries with the default value for every new key added (or remove accordingly). Your updates should hardly notice but you can still switch back to the default in O(1).

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.

How to select a random key from a HashMap in Java?

I'm working with a large ArrayList<HashMap<A,B>>, and I would repeatedly need to select a random key from a random HashMap (and do some stuff with it). Selecting the random HashMap is trivial, but how should I select a random key from within this HashMap?
Speed is important (as I need to do this 10000 times and the hashmaps are large), so just selecting a random number k in [0,9999] and then doing .next() on the iterator k times, is really not an option. Similarly, converting the HashMap to an array or ArrayList on every random pick is really not an option. Please, read this before replying.
Technically I feel that this should be possible, since the HashMap stores its keys in an Entry[] internally, and selecting at random from an array is easy, but I can't figure out how to access this Entry[]. So any ideas to access the internal Entry[] are more than welcome. Other solutions (as long as they don't consume linear time in the hashmap size) are also welcome of course.
Note: heuristics are fine, so if there's a method that excludes 1% of the elements (e.g. because of multi-filled buckets) that's no problem at all.
from top of my head
List<A> keysAsArray = new ArrayList<A>(map.keySet())
Random r = new Random()
then just
map.get(keysAsArray.get(r.nextInt(keysAsArray.size()))
I managed to find a solution without performance loss. I will post it here since it may help other people -- and potentially answer several open questions on this topic (I'll search for these later).
What you need is a second custom Set-like data structure to store the keys -- not a list as some suggested here. Lists-like data structures are to expensive to remove items from. The operations needed are adding/removing elements in constant time (to keep it up-to-date with the HashMap) and a procedure to select the random element. The following class MySet does exactly this
class MySet<A> {
ArrayList<A> contents = new ArrayList();
HashMap<A,Integer> indices = new HashMap<A,Integer>();
Random R = new Random();
//selects random element in constant time
A randomKey() {
return contents.get(R.nextInt(contents.size()));
}
//adds new element in constant time
void add(A a) {
indices.put(a,contents.size());
contents.add(a);
}
//removes element in constant time
void remove(A a) {
int index = indices.get(a);
contents.set(index,contents.get(contents.size()-1));
indices.put(contents.get(index),index);
contents.remove((int)(contents.size()-1));
indices.remove(a);
}
}
You need access to the underlying entry table.
// defined staticly
Field table = HashMap.class.getDeclaredField("table");
table.setAccessible(true);
Random rand = new Random();
public Entry randomEntry(HashMap map) {
Entry[] entries = (Entry[]) table.get(map);
int start = rand.nextInt(entries.length);
for(int i=0;i<entries.length;i++) {
int idx = (start + i) % entries.length;
Entry entry = entries[idx];
if (entry != null) return entry;
}
return null;
}
This still has to traverse the entries to find one which is there so the worst case is O(n) but the typical behaviour is O(1).
Sounds like you should consider either an ancillary List of keys or a real object, not a Map, to store in your list.
As #Alberto Di Gioacchino pointed out, there is a bug in the accepted solution with the removal operation. This is how I fixed it.
class MySet<A> {
ArrayList<A> contents = new ArrayList();
HashMap<A,Integer> indices = new HashMap<A,Integer>();
Random R = new Random();
//selects random element in constant time
A randomKey() {
return contents.get(R.nextInt(contents.size()));
}
//adds new element in constant time
void add(A item) {
indices.put(item,contents.size());
contents.add(item);
}
//removes element in constant time
void remove(A item) {
int index = indices.get(item);
contents.set(index,contents.get(contents.size()-1));
indices.put(contents.get(index),index);
contents.remove(contents.size()-1);
indices.remove(item);
}
}
I'm assuming you are using HashMap as you need to look something up at a later date?
If not the case, then just change your HashMap to an Array/ArrayList.
If this is the case, why not store your objects in a Map AND an ArrayList so you can look up randomly or by key.
Alternatively, could you use a TreeMap instead of HashMap? I don't know what type your key is but you use TreeMap.floorKey() in conjunction with some key randomizer.
After spending some time, I came to the conclusion that you need to create a model which can be backed by a List<Map<A, B>> and a List<A> to maintain your keys. You need to keep the access of your List<Map<A, B>> and List<A>, just provide the operations/methods to the caller. In this way, you will have the full control over implementation, and the actual objects will be safer from external changes.
Btw, your questions lead me to,
Why does the java.util.Set<V> interface not provide a get(Object o) method?, and
Bimap: I was trying to be clever but, of course, its values() method also returns Set.
This example, IndexedSet, may give you an idea about how-to.
[edited]
This class, SetUniqueList, might help you if you decide to create your own model. It explicitly states that it wraps the list, not copies. So, I think, we can do something like,
List<A> list = new ArrayList(map.keySet());
SetUniqueList unikList = new SetUniqueList(list, map.keySet);
// Now unikList should reflect all the changes to the map keys
...
// Then you can do
unikList.get(i);
Note: I didn't try this myself. Will do that later (rushing to home).
Since Java 8, there is an O(log(N)) approach with O(log(N)) additional memory: create a Spliterator via map.entrySet().spliterator(), make log(map.size()) trySplit() calls and choose either the first or the second half randomly. When there are say less than 10 elements left in a Spliterator, dump them into a list and make a random pick.
If you absolutely need to access the Entry array in HashMap, you can use reflection. But then your program will be dependent on that concrete implementation of HashMap.
As proposed, you can keep a separate list of keys for each map. You would not keep deep copies of the keys, so the actual memory denormalisation wouldn't be that big.
Third approach is to implement your own Map implementation, the one that keeps keys in a list instead of a set.
How about wrapping HashMap in another implementation of Map? The other map maintains a List, and on put() it does:
if (inner.put(key, value) == null) listOfKeys.add(key);
(I assume that nulls for values aren't permitted, if they are use containsKey, but that's slower)

Categories