15

It seems Both merge and compute Map methods are created to reduce if("~key exists here~") when put. My problem is: add to map a [key, value] pair when I know nothing: neither key existing in map nor it exist but has value nor value == null nor key == null.

words.forEach(word ->
        map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)
);
words.forEach(word ->
        map.merge(word, 1, (prev, one) -> prev + one)
);

Is the only difference 1 is moved from Bifunction to parameter? What is better to use? Does any of merge, compute suggests key/val are existing? And what is essential difference in use case of them?

J.J. Beam
  • 2,612
  • 2
  • 26
  • 55

2 Answers2

20

The documentation of Map#compute(K, BiFunction) says:

Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). For example, to either create or append a String msg to a value mapping:

map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))

(Method merge() is often simpler to use for such purposes.)

If the remapping function returns null, the mapping is removed (or remains absent if initially absent). If the remapping function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.

The remapping function should not modify this map during computation.

And the documentation of Map#merge(K, V, BiFunction) says:

If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null. This method may be of use when combining multiple mapped values for a key. For example, to either create or append a String msg to a value mapping:

map.merge(key, msg, String::concat)

If the remapping function returns null, the mapping is removed. If the remapping function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.

The remapping function should not modify this map during computation.

The important differences are:

  • For compute(K, BiFunction<? super K, ? super V, ? extends V>):

    • The BiFunction is always invoked.
    • The BiFunction accepts the given key and the current value, if any, as arguments and returns a new value.
    • Meant for taking the key and current value (if any), performing an arbitrary computation, and returning the result. The computation may be a reduction operation (i.e. merge) but it doesn't have to be.
  • For merge(K, V, BiFunction<? super V, ? super V, ? extends V>):

    • The BiFunction is invoked only if the given key is already associated with a non-null value.
    • The BiFunction accepts the current value and the given value as arguments and returns a new value. Unlike with compute, the BiFunction is not given the key.
    • Meant for taking two values and reducing them into a single value.
Slaw
  • 37,820
  • 8
  • 53
  • 80
  • 2
    so it seems merge is a particular case of compute, isn't it? – J.J. Beam Sep 28 '19 at 13:15
  • 2
    It does seem that way, for the most part. But there is a major difference in that `compute` passes the key and current value to the `BiFunction` whereas `merge` passes the current value and given (i.e. new) value to the `BiFunction`. This changes how the `BiFunction` implementation is used. In other words, for `compute` it looks like the key is also meant to be, or at least can be, part of the computation but for `merge` it looks like the key is meant to be irrelevant. – Slaw Sep 28 '19 at 13:19
  • compute passes the key and current value VS merge passes the current value and given, but given should be provided at parameter - isn't it the almost same?I can access from compute's BiFunction to the value which would be sent via parameter to merge's BiFunction. (the only restriction it should be effectively final ) But it's the minor. – J.J. Beam Sep 28 '19 at 13:34
  • major is: the is key mandatory to be in map while merge & associate to non-null value, right?but when compute key doesn't even need to be in map? – J.J. Beam Sep 28 '19 at 13:36
  • 1
    It's only _similar_ when you implement the `BiFunction` as a lambda, with all the scoped information available. But what if you use an implementation that doesn't have access to the local variables or the fields of the enclosing instance? And no, neither `merge` nor `compute` require a mapping to be present at the time of invocation. In the case of `merge`, if the key is not present or is associated with a null value then the given value is immediately associated with the key (without invoking the `BiFunction`). – Slaw Sep 28 '19 at 13:39
  • Great! Thank you, and the last detail: if the key == null, it the same as I try Map#put( null, value) right? – J.J. Beam Sep 28 '19 at 14:10
  • 1
    Yes, the documentation of both `merge` and `compute` say a `NullPointerException` will be thrown "_if the specified key is null and this map does not support null keys_", which is the same behavior as `put` regarding keys. – Slaw Sep 28 '19 at 14:33
0

If the mapping function, as in your case, only depends on the current mapped value, then you can use both. But I would prefer:

  • compute if you can guarantee that a value for the given key exists. In this case the extra value parameter taken by the merge method is not needed.
  • merge if it is possible that no value for the given key exists. In this case merge has the advantage that null does NOT have to be handled by the mapping function.
Stefan Feuerhahn
  • 1,564
  • 1
  • 14
  • 22