Learn how Clojure's vectors, maps, sets, and lists behave, and how updates create new values without in-place mutation.
Once you accept immutability as the default, the next question is practical:
In Clojure, the answer is usually:
All of them are immutable and persistent, but they are not interchangeable. A Java engineer becomes much more effective once these collections feel like normal tools rather than exotic functional artifacts.
Most business data in Clojure is modeled with maps.
1(def order
2 {:order/id 1001
3 :status :pending
4 :customer {:customer/id 77
5 :email "ada@example.com"}
6 :items [{:sku "A-1" :qty 2 :unit-price 15M}]})
Why maps show up everywhere:
Typical operations:
1(assoc order :status :paid)
2(dissoc order :temporary/token)
3(update order :items conj {:sku "B-2" :qty 1 :unit-price 9M})
4(assoc-in order [:customer :vip?] true)
Each expression returns a new map-based value. The original order remains available.
Vectors are the most common choice when you want an ordered collection of items:
1[{:sku "A-1" :qty 2}
2 {:sku "B-2" :qty 1}]
They are a good fit for:
Common operations:
1(conj [:draft :pending] :paid)
2;; => [:draft :pending :paid]
3
4(assoc [:a :b :c] 1 :beta)
5;; => [:a :beta :c]
If you come from Java, think of vectors as the general-purpose “array-list-like” collection, except updates return a new vector instead of mutating the old one.
Sets shine when the question is:
Example:
1(def order-flags #{:fraud-review :priority})
2
3(contains? order-flags :priority)
4;; => true
5
6(conj order-flags :gift)
7;; => #{:fraud-review :priority :gift}
This is often clearer than a map of booleans or a list you repeatedly scan for membership.
ListThis is an early source of confusion.
Clojure lists are linked lists, not the moral equivalent of java.util.List or ArrayList.
Example:
1'(:validate :price :persist)
Lists are most natural for:
In ordinary application data, you will usually reach for vectors first. Most Java developers over-assume that “list” must be the default collection. In Clojure, that instinct is usually wrong.
This distinction matters.
In Java, an “unmodifiable” collection is often just a wrapper around something that may still be mutable elsewhere.
In Clojure, the collection value itself is immutable. That means:
That is a stronger and more useful guarantee than “please do not call mutating methods.”
Java code often updates state by reaching into an object and mutating a field.
Clojure updates nested data by returning a new value:
1(def updated-order
2 (update-in order [:items 0 :qty] inc))
That style can feel unfamiliar for a week or two. Then it starts to feel cleaner because the data flow is explicit:
No hidden setter calls. No aliasing surprises.
Use a:
That small decision table is enough for most early Clojure work.