Use Clojure vectors as the default ordered collection for indexed, immutable data, with practical guidance on conj, assoc, subvec, sequence functions, and Java ArrayList comparisons.
Vectors are the default ordered collection for most Clojure application data. If a Java engineer reaches for ArrayList, the first Clojure collection to consider is usually a vector.
Vectors are immutable, persistent, ordered, and indexed. Updating a vector returns a new vector that shares structure with the old one.
1(def numbers [10 20 30])
2
3(get numbers 1)
4;; => 20
5
6(nth numbers 1)
7;; => 20
8
9(conj numbers 40)
10;; => [10 20 30 40]
11
12(assoc numbers 1 25)
13;; => [10 25 30]
14
15numbers
16;; => [10 20 30]
The last expression matters: numbers did not change.
Java ArrayList habit |
Clojure vector form | Difference |
|---|---|---|
list.get(i) |
(get v i) or (nth v i) |
Returns a value without mutation |
list.add(x) |
(conj v x) |
Returns a new vector with x at the end |
list.set(i, x) |
(assoc v i x) |
Returns a new vector |
list.subList(a, b) |
(subvec v a b) |
Returns a vector view-like value |
| Loop and append to new list | mapv, filterv pattern with into [] |
Expresses result construction directly |
Vectors provide efficient indexed access and efficient end appends in practice. Internally they are persistent tree structures, not resizable arrays, so think “immutable indexed value” rather than “array with a bigger capacity.”
get vs nth vs Function CallVectors can be used as functions of their index:
1([10 20 30] 1)
2;; => 20
That is legal, but often less readable than get or nth.
| Form | Missing index behavior | Good use |
|---|---|---|
(get v i) |
Returns nil or a default |
Safe lookup |
(get v i default) |
Returns default |
Safe lookup with fallback |
(nth v i) |
Throws when out of range | Assertive positional access |
(v i) |
Throws when out of range | Compact, but use sparingly |
Example:
1(get [10 20 30] 9 :missing)
2;; => :missing
Use conj when adding one or more values:
1(conj [1 2] 3 4)
2;; => [1 2 3 4]
Use into when adding many values from another collection:
1(into [1 2] [3 4 5])
2;; => [1 2 3 4 5]
Use mapv when the result should be a vector:
1(mapv #(* % %) [1 2 3])
2;; => [1 4 9]
Plain map returns a lazy sequence:
1(map #(* % %) [1 2 3])
2;; => (1 4 9)
That is useful when you want laziness, but choose mapv, vec, or into [] when the caller expects a vector.
assoc replaces by index:
1(assoc [:draft :submitted :paid] 1 :reviewed)
2;; => [:draft :reviewed :paid]
update reads the existing value and applies a function:
1(update [10 20 30] 1 inc)
2;; => [10 21 30]
This is the vector equivalent of “read, transform, write back,” but without mutating the original vector.
| Use a vector when… | Consider another collection when… |
|---|---|
| Order matters | Membership is the main operation |
| Index matters | Keys are domain labels |
| You need a stable result collection | You need code-as-data lists |
| You append at the end | You repeatedly add/remove at the front |
ArrayList loop that builds a result list using mapv.(conj [1 2 3] 4) with (conj '(1 2 3) 4).update to increment the second value in [10 20 30].(get v i) or (nth v i) is better when the index may be missing.conj adds to the end of a vector.assoc and update return new vectors.mapv, into [], or vec when you need a concrete vector result.ArrayList habits into Clojure core logic.