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()
andRemove(key, value)
. - In C++, there are
emplace()
,try_emplace()
,insert_or_assign()
, etc. inmap
andunordered_map
containers.
Why
editUsing 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
editSome 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
editStructural search in IDEs on the IntelliJ platform (Java, C#, C++)
editStructural 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- ↑ No
put()
orremove()
calls on aConcurrentHashMap
afterget()
orcontainsKey()
? in the code review checklist for Java concurrency - ↑ 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