Search code examples
javadictionaryclojure

What's the equivalent of Java's computeIfAbsent or putIfAbsent in Clojure?


With a map in Java you can write map.computeIfAbsent(key, f) that checks whether the key exists in the map already, and if not calls f on the key to generate a value, and enters that in the map. You also have map.putIfAbsent(key, value) which only puts the value in the map if the key does not already exist in the map.

Ignoring the fact the Java methods also return the value, and instead wanting the new map returned, what would be the equivalent code in Clojure?

The best I've come up with so far is to roll my own with something like

(defn compute-if-absent [map key f]
  (if (key map)
    map
    (assoc map key (f key))))

Is there an alternative to rolling my own?


Solution

  • Clojure maps are immutable so you will always return a new map with updated contents - thus your functions will be always thread safe. It also means that you can't have a global variable holding your map and mutate them in place.

    Your implementation of compute-if-absent is almost correct. It will fail for keys that are falsey, for example {false 1}. You need to change your if condition and use contains? instead of key:

    (defn compute-if-absent [m k f]
      (if (contains? m k)
        m
        (assoc m k (f k))))
    

    If you do need to have the same behaviour as ConcurrentMap you can just use them using Java interop in Clojure.