Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should you check if the map containsKey before using ConcurrentMap's putIfAbsent

I have been using Java's ConcurrentMap for a map that can be used from multiple threads. The putIfAbsent is a great method and is much easier to read/write than using standard map operations. I have some code that looks like this:

ConcurrentMap<String, Set<X>> map = new ConcurrentHashMap<String, Set<X>>();  // ...  map.putIfAbsent(name, new HashSet<X>()); map.get(name).add(Y); 

Readability wise this is great but it does require creating a new HashSet every time even if it is already in the map. I could write this:

if (!map.containsKey(name)) {     map.putIfAbsent(name, new HashSet<X>()); } map.get(name).add(Y); 

With this change it loses a bit of readability but does not need to create the HashSet every time. Which is better in this case? I tend to side with the first one since it is more readable. The second would perform better and may be more correct. Maybe there is a better way to do this than either of these.

What is the best practice for using a putIfAbsent in this manner?

like image 362
Chris Dail Avatar asked Sep 20 '10 14:09

Chris Dail


People also ask

How does map containsKey work?

Map. containsKey() method is used to check whether a particular key is being mapped into the Map or not. It takes the key element as a parameter and returns True if that element is mapped in the map.

Is ConcurrentHashMap thread safe?

ConcurrentHashMap class is thread-safe i.e. multiple threads can operate on a single object without any complications. At a time any number of threads are applicable for a read operation without locking the ConcurrentHashMap object which is not there in HashMap.


1 Answers

Concurrency is hard. If you are going to bother with concurrent maps instead of straightforward locking, you might as well go for it. Indeed, don't do lookups more than necessary.

Set<X> set = map.get(name); if (set == null) {     final Set<X> value = new HashSet<X>();     set = map.putIfAbsent(name, value);     if (set == null) {         set = value;     } } 

(Usual stackoverflow disclaimer: Off the top of my head. Not tested. Not compiled. Etc.)

Update: 1.8 has added computeIfAbsent default method to ConcurrentMap (and Map which is kind of interesting because that implementation would be wrong for ConcurrentMap). (And 1.7 added the "diamond operator" <>.)

Set<X> set = map.computeIfAbsent(name, n -> new HashSet<>()); 

(Note, you are responsible for the thread-safety of any operations of the HashSets contained in the ConcurrentMap.)

like image 69
Tom Hawtin - tackline Avatar answered Sep 18 '22 11:09

Tom Hawtin - tackline