combine putIfAbsent and replace with ConcurrentMap

后端 未结 4 2068
长情又很酷
长情又很酷 2021-02-04 03:54

I have a usecase where I have to

  • insert a new value if the key does not exist in the ConcurrentHashMap
  • replace the old value with a new value if the key a
4条回答
  •  隐瞒了意图╮
    2021-02-04 04:18

    You can use MutableMapIterable.updateValueWith(K key, Function0 factory, Function2 function, P parameter) from Eclipse Collections.

    The factory argument creates an initial value if none is in the map. The function argument is applied to the map value along with an additional parameter to come up with a new map value. That parameter is passed as the final argument to updateValueWith(). The function is called even in the case where the key wasn't in the map. So the initial value is really the function applied to the output of factory and parameter. The function must not mutate the value; it should return a new value. In your example, the map values are Strings which are immutable so we're fine.

    In ConcurrentMaps like org.eclipse.collections.impl.map.mutable.ConcurrentHashMap, the implementation of updateValueWith() is also thread-safe and atomic. It’s important that function does not mutate the map values or it wouldn’t be thread-safe. It should return new values instead. In your example, the map values are Strings which are immutable so we're fine.

    If your method recalculateNewValue() just does String concatenation, here's how you might use updateValueWith().

    Function0 factory = () -> "initial ";
    Function2 recalculateNewValue = String::concat;
    
    MutableMap map = new ConcurrentHashMap<>();
    map.updateValueWith("test", factory, recalculateNewValue, "append1 ");
    Assert.assertEquals("initial append1 ", map.get("test"));
    map.updateValueWith("test", factory, recalculateNewValue, "append2");
    Assert.assertEquals("initial append1 append2", map.get("test"));
    

    You can use Java 8's ConcurrentMap.compute(K key, BiFunction remappingFunction) to accomplish the same thing, but it has a few disadvantages.

    ConcurrentMap map = new ConcurrentHashMap<>();
    map.compute("test", (key, oldValue) -> oldValue == null ? "initial append1 " : oldValue + "append1 ");
    Assert.assertEquals("initial append1 ", map.get("test"));
    map.compute("test", (key, oldValue) -> oldValue == null ? "initial append1 " : oldValue + "append2");
    Assert.assertEquals("initial append1 append2", map.get("test"));
    
    • There's no separate factory to handle the case of absent keys so the body of the lambda has to deal with values and initial values.
    • The API isn't amenable to reusing lambdas. Every call to updateValueWith() shares the same lambdas, but every call to compute() creates new garbage on the heap.

    Note: I am a committer for Eclipse Collections

提交回复
热议问题