Search code examples
javaconcurrencyrace-conditionjava.util.concurrentconcurrenthashmap

How to remove Set, which is used as value, from ConcurrentHashMap<String, Set<String>>?


Let's consider the following following code:

ConcurrentHashMap<String, Set<String>> map = new ConcurrentHashMap<>();

// Add element: {mapKey, setValue}

map.computeIfAbsent(mapKey, new Function<String, Set<String>>() {
    @Override
    public Set<String> apply(String mapK) {
        return ConcurrentHashMap.newKeySet();
    }
}).add(setValue);

// Remove element: {mapKey, setValue}

Set<String> updatedSet = map.computeIfPresent(mapKey, new BiFunction<String, Set<String>, Set<String>>() {
    @Override
    public Set<String> apply(String mapK, Set<String> old) {
        old.remove(setValue);
        return old;
    }
});

// I need remove mapKey, but I cannod do this like this, because of race condition bug
if (updatedSet.isEmpty()) {
    map.remove(mapKey);
}

So, what we can see:

  1. We have ConcurrentHashMap<String, Set<String>> map, where key of map is String, and value is ConcurrentHashSet.
  2. I need to remove set, which is value of map, when set is empty.
  3. I cannot implement naive removing of set, because of race condition bug.

Is there any brilliant solution of my problem?


Solution

  • computeIfPresent removes the entry if the mapper returns null. Instead of performing the removal in a separate step, return null from the mapper if you want to remove the entry.

    (Also, you should really fold the .add(setValue) into your computeIfAbsent mapper, and use compute instead of computeIfAbsent, because you're not doing anything to protect the add call right now. Using merge would also be an option.)