简体   繁体   中英

How to implement atomic getOrDefaultWithPut() based on getOrDefault() in ConcurrentHashMap?

ConcurrentHashMap supports atomic getOrDefault(Object key, V defaultValue) , which

Returns the value to which the specified key is mapped, or the given default value if this map contains no mapping for the key.

My problem is:

How can I enhance ConcurrentHashMap by providing an atomic operation, say getOrDefaultWithPut(Object key, V defaultValue) , which

"Returns the value to which the specified key is mapped, or first put the the given default value into the map and then return the default value if this map contains no mapping for the key.`"


My solution:

Currently I have a Wrapper class of

 private ConcurrentMap<K, V> map = new ConcurrentHashMap<>();

The method is:

public synchronized K getOrDefaultWithPut(K key, V defaultValue)
{
    map.putIfAbsent(key, defaultValue);
    return map.get(key);
}
  1. Is this implementation thread-safe?
  2. Is synchronized necessary? What bad would happen if it is removed?
  3. If getOrDefaultWithPut(K key, V defaultValue) is the only public method of Wrapper , is this implementation thread-safe and is synchronized necessary?

Just use computeIfAbsent

If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unless null . The entire method invocation is performed atomically [...]

Returns

the current (existing or computed) value associated with the specified key, or null if the computed value is null

Provide a mappingFunction which generates your default value and this will satisfy your requirement.

Returns the value to which the specified key is mapped

That's the existing value.

or first put the the given default value into the map and then return the default value if this map contains no mapping for the key

The method will insert the computed value (the default), if not null , and return that computed value.


You won't need synchronized with this solution. The method invocation is atomic and it's therefore thread safe.

Almost. You should use the value, returned by putIfAbsent instead of doing another .get , and you don't need it synchronized (it's fairly useless in this case anyway, unless all other callers know to synchronize as well, which defeats the purpose of it being ConcurrentHashMap :

public V getOrDefaultWithPut(K key, V defaultValue) 
{
  V val = map.putIfAbsent(key, defaultValue);
  return val == null ? defaultValue : val;
}

Edit : computeIfAbsent works too, this is just a little bit simpler, since you don't need to define a function in this case, and is also, I think, more informative as an answer, as it actually fixes the problem with your original approach, rather than replacing it with a different one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM