Browse Learn Clojure Foundations as a Java Developer

Maps in Clojure

Use Clojure maps as immutable records and lookup tables with keyword keys, assoc, update, dissoc, merge, nested updates, and Java HashMap comparisons.

Maps are central to everyday Clojure. They often replace a mix of Java DTOs, HashMaps, small configuration classes, and ad hoc records.

The key shift is that Clojure maps are values. You do not mutate them in place; you derive new maps.

Map Literals

1(def user
2  {:user/id 42
3   :user/email "dev@example.com"
4   :user/status :active})

Keywords are the usual internal keys because they are compact, self-evaluating, and readable. Qualified keywords such as :user/id help larger systems avoid collisions.

Lookup

You have several idiomatic lookup options:

1(get user :user/email)
2;; => "dev@example.com"
3
4(:user/email user)
5;; => "dev@example.com"
6
7(user :user/email)
8;; => "dev@example.com"

Keyword-as-function is common for simple field access. get is clearer when you want a default:

1(get user :user/role :guest)
2;; => :guest

Updating Maps

Operation Example Result
Add or replace (assoc user :user/name "Ada") New map with name
Remove (dissoc user :user/status) New map without status
Transform existing value (update user :user/email str/lower-case) New map with lowercased email
Nested replace (assoc-in order [:customer :email] email) New nested map
Nested transform (update-in order [:totals :cents] + 100) New nested map

Example with a namespace require:

1(require '[clojure.string :as str])
2
3(update user :user/email str/lower-case)
4;; => {:user/id 42
5;;     :user/email "dev@example.com"
6;;     :user/status :active}

The original user map is unchanged.

Java HashMap Comparison

Java HashMap habit Clojure map form Difference
put(k, v) (assoc m k v) Returns a new map
remove(k) (dissoc m k) Returns a new map
get(k) (get m k) or (k m) when k is a keyword No casting ceremony in typical Clojure code
putAll(other) (merge m other) Later maps win
Nested mutable objects assoc-in, update-in Updates nested values immutably

In Java, the object identity often stays the same while contents change. In Clojure, the value changes and the old value remains available.

Merge and Conflict Rules

merge combines maps. Later values win:

1(merge {:user/name "Ada"
2        :user/status :draft}
3       {:user/status :active
4        :user/email "ada@example.com"})
5;; => {:user/name "Ada"
6;;     :user/status :active
7;;     :user/email "ada@example.com"}

Use merge-with when conflicts should be combined:

1(merge-with + {:errors 2 :warnings 1}
2              {:errors 3 :info 5})
3;; => {:errors 5 :warnings 1 :info 5}

Filtering Maps

Maps are sequences of entries when traversed:

1(seq {:a 1 :b 2})
2;; => ([:a 1] [:b 2])

To filter and keep a map result, use into {}:

1(into {}
2      (filter (fn [[_ v]] (pos? v)))
3      {:a 1 :b -2 :c 3})
4;; => {:a 1 :c 3}

Do not forget into {}. Plain filter returns a sequence of map entries, not a map.

Maps as Domain Records

Clojure maps work well for open domain data:

1(defn activate-user [user]
2  (assoc user :user/status :active))
3
4(defn active-user? [user]
5  (= :active (:user/status user)))

You can add schema, spec, Malli, or other validation later when the boundary needs stronger guarantees. The core representation can still be plain data.

Practice

  1. Convert a Java DTO with id, email, and status into a qualified-key Clojure map.
  2. Use assoc, update, and dissoc without mutating the original map.
  3. Use merge-with to combine two count maps.
  4. Filter a map to keep only positive values and return a map, not a sequence.

Key Takeaways

  • Maps are the normal Clojure shape for structured domain data.
  • Prefer qualified keywords for internal map keys in larger systems.
  • assoc, dissoc, update, and merge return new maps.
  • Traversing a map yields entries; use into {} when you need a map result.
  • Maps reduce boilerplate compared with many Java DTO and HashMap patterns.

Quiz: Maps in Clojure

### What does `assoc` return? - [x] A new map with the key added or replaced. - [ ] The same map mutated in place. - [ ] A Java `HashMap`. - [ ] A lazy sequence. > **Explanation:** Clojure maps are immutable, so `assoc` returns a new map value. ### What is the common internal key type for Clojure maps? - [x] Keywords. - [ ] Mutable Java fields. - [ ] Threads. - [ ] Raw byte arrays. > **Explanation:** Keywords are compact self-evaluating labels and are commonly used as Clojure map keys. ### What does plain `filter` return when used on a map? - [x] A sequence of map entries. - [ ] A map automatically. - [ ] A set automatically. - [ ] A Java stream. > **Explanation:** Maps are traversed as entries, so use `into {}` when a filtered map result is needed. ### Which function updates a nested value? - [x] `update-in` - [ ] `println` - [ ] `keyword` - [ ] `list` > **Explanation:** `update-in` transforms a value at a nested path and returns a new map. ### True or False: `merge` mutates the first map. - [ ] True - [x] False > **Explanation:** `merge` returns a new map. Later maps win when keys conflict.
Revised on Saturday, May 23, 2026