You can use MutableMapIterable.updateValueWith(K key, Function0<? extends V> factory, Function2<? super V,? super P,? extends V> 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<String> factory = () -> "initial ";
Function2<String, String, String> recalculateNewValue = String::concat;
MutableMap<String, String> 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<String, String> 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