Improving PutIfAbsent…

Since Java 5 we have ConcurrentMap interface with a couple new methods, one of which is:

public V putIfAbsent(K key, V val);

This essentially replaces the following code:

if (!map.containsKey(key))
    return map.put(key, value);
else
    return map.get(key);

Although this method is useful sometime I found several nagging problems with similar methods:

  1. It doesn’t allow chaining as it returns null if value was actually added, and
  2. It forces me to create value (to pass as a parameter) every time even though it will be ignored all times except for the first time when it’s added.

In GridGain we’ve remedied this problem (setting aside atomicity of the putIfAbsent – our version requires outside synchronization) by having a utility method that, by the way, will be publicly available as part of new functional APIs added to GridGain 3.0:

public static <K, V> V mapGet(@NotNull Map<K, V> map, @Nullable K key, @Nullable GridFactoryClosure<V> c) {
    assert map != null;

    // 'null' values are allowed...
    if (map.containsKey(key) == false) {
        V v = c == null ? null : c.apply();

        map.put(key, v);

        return v;
    }
    else {
        return map.get(key);
    }
}

Now this version have several clear advantages:

  1. It has name mapGet to reflect that it is in fact a getter with an option to handle the key-miss;
  2. It allows for chaining as it always returns whatever value is in the map after this method completes, and
  3. It utilizes factory-closure to avoid unnecessary object creation (see below for example).

Factory-closure is a simple closure that takes no parameters and returns new value every time its apply method is called. The same class that contains this mapGet method (GridLang.java) contains other routines that allow for very terse creation of various factory-closures. So, the final usage of mapGet method may look something like that (using typedefs from GridGain 3.0 as well):

Map<String, List<Integer>> map = ...

// Getting list value from the 'map' with the 'key' (inserting a new empty list if 
// one is not present already) and adding '2010' to the final list.
G.mapGet(map, "key", G.<Integer>newList()).add(2010);

Comparing to the usual boiler-plate code:

Map<String, List<Integer>> map = ...

// Getting list value from the 'map' with the 'key' (inserting a new empty list if 
// one is not present already) and adding '2010' to the final list.
List<Integer> list = map.get("key");

if (list == null) {
    list = new ArrayList<Integer>();

    map.put("key", list);
}

list.add(2010);

That looks pretty neat, type safe and almost “clean” except for the Integer type qualification due to limitations of Java type inferencing. And it is pretty efficient too – since method newList(...) returns a constant static factory-closure allowing to fully avoid any unnecessary object creation.

Enjoy!

Follow

Get every new post delivered to your Inbox.

Join 1,107 other followers