Software Design/Use compound operations on map objects

Checklist questions:

  • Can use a compound map operation instead of a sequence of "primitive" operations with the same key?

Maps (dictionaries, associative containers) in many programming languages support compound operations that replace sequences of "primitive" operations like getting a value by a key, putting a value for a key, and removing a key from the map.

  • In Java, there are Map.putIfAbsent(), compute(), merge(), etc.
  • In C#, there are IDictionary.TryAdd() and Remove(key, value).
  • In C++, there are emplace(), try_emplace(), insert_or_assign(), etc. in map and unordered_map containers.

Why edit

Using compound map operations results in shorter, clearer and less error-prone code because the key is passed as an argument only once:

// key1, key2 are in scope
if (!map.containsKey(key1)) {
  map.put(key1, value) // Opportunity to mistakenly use key2
}
// vs.
map.getOrPut(key1, { value })

The semantics of a compound map operation are more apparent than the semantics of a series of "primitive" map operations.

Compound operations might also be faster than equivalent series of "primitive" operations because the implementations can perform only a single lookup in the map during the compound operation.

In concurrent code, using compound, "atomic" updates to a map becomes not just a matter of good style but also of the correctness of the program. For example, see a corresponding item[1] in the Java concurrency checklist.

Why not edit

Some compound operations on maps accept function objects (see, for example, Map.compute() in Java). These function objects may be allocated on heap and may later need to be cleaned up by the garbage collector. This may be undesirable in some applications, e. g. those targeting nearly zero garbage allocation rate.

Static enforcement edit

Structural search in IDEs on the IntelliJ platform (Java, C#, C++) edit

Structural search in IDEs on the IntelliJ platform allows to catch series of map operations which could be replaced by compound operations.

Pattern to find code replaceable with Map.putIfAbsent() or computeIfAbsent() in Java:

if (!$map$.containsKey($k$)) {
    $map$.put($k$, $v$);
}

It's possible to turn a structural search pattern into an inspection.[2]

References edit

  1. No put() or remove() calls on a ConcurrentHashMap after get() or containsKey()? in the code review checklist for Java concurrency
  2. Create custom inspections - IntelliJ IDEA Help

Sources edit

  • Boswell, Dustin; Foucher, Trevor (2011). The Art of Readable Code. ISBN 978-0596802295.  Chapter 13, "Be Familiar with the Libraries Around You" section