A data structure that I use commonly in multi-threaded applications is a ConcurrentHashMap where I want to save a group of items that all share the same key. The problem occurs when installing the first item for a particular key value.
The pattern that I have been using is:
final ConcurrentMap<KEYTYPE, Set<VALUETYPE>> hashMap = new ConcurrentHashMap<KEYTYPE, Set<VALUETYPE>>();
// ...
Set<VALUETYPE> newSet = new HashSet<VALUETYPE>();
final Set<VALUETYPE> set = hashMap.putIfAbsent(key, newSet)
if (set != null) {
newSet = set;
}
synchronized (newSet) {
if (!newSet.contains(value)) {
newSet.add(value);
}
}
Is there a better pattern for doing this operation? Is this even thread-safe? Is there a better class to use for the inner Set than java.util.HashSet?
I strongly recommend using the Google Guava libraries for this, specifically an implementation of Multimap. The HashMultimap would be your best bet, though if you need concurrent update opterations you would need to wrap it in a delegate using Multimaps.synchronizedSetMultimap().
Another option is to use a
ComputingMap(also from Guava), which is a map that, if the Value returned from a call toget(Key)does not exist, it is instantiated there and then.ComputingMaps are created using MapMaker.The code from your question would be roughly:
The
Functionwould only be called when a call toget()for a specific key would otherwise return null. This means that you can then do this:safely knowing that the
HashSet<VALUETYPE>is created if it doesn’t already exist.MapMakeris also relevant because of the control it gives you over the tuning of the returned Map, letting you specify, for example, the concurrency level using the methodconcurrencyLevel(). You may find that useful: