Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why java Map.merge does not pass a supplier?

Tags:

java-8

I want in java a method which allows me to modify a value if exist, or insert one if it doesn't. Similar to merge, but:

  1. I want to pass a value supplier and not a value, to avoid creating it when not needed
  2. In case the value exists, I don't want to reinsert it nor remove it, just access its methods with a container.

I had to write this. The problem with writing it myself is that the version for Concurrent maps is not trivial

public static <K, V> V putOrConsume(Map<K, V> map, K key, Supplier<V> ifAbsent, Consumer<V> ifPresent) {
    V val = map.get(key);
    if (val != null) {
      ifPresent.accept(val);
    } else {
      map.put(key, ifAbsent.get());
    }
    return val;
}
like image 439
Daniel Pinyol Avatar asked Apr 20 '26 23:04

Daniel Pinyol


1 Answers

The best "standard" way of achieving it is to use compute():

 Map<String, String> map = new HashMap<>();

 BiFunction<String, String, String> convert = (k, v) -> v == null ? "new_" + k : "old_" + v;

 map.compute("x", convert);
 map.compute("x", convert);

 System.out.println(map.get("x")); //prints old_new_x

Now, say, you have your Supplier and Consumer and would like to follow DRY principle. Then you could use a simple function combinator:

 Map<String, String> map = new HashMap<>();

 Supplier<String> ifAbsent = () ->  "new";
 Consumer<String> ifPresent = System.out::println;

 BiFunction<String, String, String>  putOrConsume = (k, v) -> {
        if (v == null) return ifAbsent.get();
        ifPresent.accept(v);
        return v;
 };
 map.compute("x", putOrConsume); //nothing
 map.compute("x", putOrConsume); //prints "new"

Obviously, you could write a combinator function that takes supplier and consumer and returns BiFunction to make the code above even more generic.

The drawback of this proposed approach is in the extra call to map.put() even if you simply consume the value, i.e. it will be slightly slower, by the time of key lookup. The good news are, map implementations will simply replace the value without creating the new node. I.e. no new objects will be created or garbage collected. Most of the time such trade-offs are justified.

map.compute(...) and map.putIfAbsent(...) are much more powerful than fairly specialized proposed putOrConsume(...). It is so asymmetrical I would actually review the reasons why you need it in the code.

like image 56
Alex Pakka Avatar answered Apr 27 '26 15:04

Alex Pakka



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!