向量 #
一、向量简介 #
向量是Clojure中最常用的数据结构,类似于其他语言中的数组。它提供了高效的随机访问和尾部添加操作。
1.1 向量特点 #
- 类似数组的索引结构
- 随机访问O(log32 n) ≈ O(1)
- 尾部添加O(1)
- 不可变、持久化
- 32叉树实现
1.2 创建向量 #
clojure
[1 2 3 4 5]
(vector 1 2 3)
(vec '(1 2 3))
(vec #{1 2 3})
(vector-of :int 1 2 3)
[]
clojure
(class [1 2 3])
二、基本操作 #
2.1 访问元素 #
clojure
(def v [1 2 3 4 5])
(nth v 0)
(nth v 4)
(nth v 10)
(nth v 10 :not-found)
(v 2)
(get v 2)
(get v 10)
(get v 10 :default)
(first v)
(last v)
2.2 添加元素 #
clojure
(conj [1 2 3] 4)
(conj [1 2 3] 4 5 6)
(into [1 2 3] [4 5 6])
(cons 0 [1 2 3])
conj 对向量在尾部添加元素。
2.3 修改元素 #
clojure
(def v [1 2 3 4 5])
(assoc v 2 :x)
(assoc v 0 :a 4 :b)
(update v 0 inc)
(update v 2 * 10)
2.4 删除元素 #
clojure
(def v [1 2 3 4 5])
(pop v)
(subvec v 1 4)
(subvec v 2)
三、向量函数 #
3.1 判断函数 #
clojure
(vector? [1 2 3])
(vector? '(1 2 3))
(vector? {:a 1})
(empty? [])
(empty? [1 2 3])
3.2 向量构建 #
clojure
(vector 1 2 3)
(vec '(1 2 3))
(vec (range 5))
(repeat 5 0)
(repeatedly 5 rand)
3.3 向量长度 #
clojure
(count [1 2 3 4 5])
(count [])
3.4 索引操作 #
clojure
(.indexOf [1 2 3 2 1] 2)
(.lastIndexOf [1 2 3 2 1] 2)
(index-of [1 2 3 2 1] 2)
(last-index-of [1 2 3 2 1] 2)
四、向量遍历 #
4.1 map #
clojure
(map inc [1 2 3 4 5])
(mapv inc [1 2 3 4 5])
(map-indexed vector [:a :b :c])
4.2 filter #
clojure
(filter even? [1 2 3 4 5 6])
(filterv even? [1 2 3 4 5 6])
(keep #(when (even? %) (* % 2)) [1 2 3 4 5])
4.3 reduce #
clojure
(reduce + [1 2 3 4 5])
(reduce conj [] [1 2 3])
4.4 for #
clojure
(for [x [1 2 3 4 5]]
(* x x))
(into []
(for [x [1 2 3 4 5]
:when (even? x)]
x))
五、解构与绑定 #
5.1 向量解构 #
clojure
(let [[a b c] [1 2 3]]
[a b c])
(let [[first & rest] [1 2 3 4 5]]
[first rest])
(let [[a b & others :as all] [1 2 3 4 5]]
{:a a :b b :others others :all all})
5.2 函数参数解构 #
clojure
(defn process [[first second & rest]]
{:first first
:second second
:rest rest})
(process [1 2 3 4 5])
5.3 循环绑定 #
clojure
(doseq [[i v] (map-indexed vector [:a :b :c])]
(println i "->" v))
(dotimes [i 5]
(println i))
六、向量与其他序列 #
6.1 转换为向量 #
clojure
(vec '(1 2 3))
(vec {:a 1 :b 2})
(vec #{1 2 3})
(into [] '(1 2 3))
(into [] (range 5))
6.2 向量vs列表 #
| 操作 | 向量 | 列表 |
|---|---|---|
| 头部添加 | O(n) | O(1) |
| 尾部添加 | O(1) | O(n) |
| 随机访问 | O(log32 n) | O(n) |
| 创建语法 | [1 2 3] |
'(1 2 3) |
选择建议:
- 需要随机访问:使用向量
- 需要头部添加:使用列表
- 一般数据存储:使用向量
七、高级操作 #
7.1 subvec #
clojure
(def v [1 2 3 4 5 6 7 8 9])
(subvec v 2 5)
(subvec v 3)
subvec 是O(1)操作,共享底层数据。
7.2 replace #
clojure
(replace {1 :a, 2 :b} [1 2 3 1 2 3])
7.3 distinct #
clojure
(distinct [1 2 1 3 2 4 3 5])
7.4 flatten #
clojure
(flatten [[1 2] [3 [4 5]] [6]])
7.5 group-by #
clojure
(group-by even? [1 2 3 4 5 6])
7.6 partition #
clojure
(partition 3 [1 2 3 4 5 6 7 8 9])
(partition-all 3 [1 2 3 4 5])
八、性能特点 #
8.1 持久化向量结构 #
Clojure向量使用32叉树实现:
text
[root]
/ | \
... ... ...
/ | \
[leaf] [leaf] [leaf]
| | |
data data data
8.2 时间复杂度 #
| 操作 | 复杂度 |
|---|---|
| 访问 nth | O(log32 n) |
| 更新 assoc | O(log32 n) |
| 添加 conj | O(1) amortized |
| 删除 pop | O(1) |
8.3 内存效率 #
clojure
(def small-v (vec (range 10)))
(def large-v (vec (range 1000000)))
(def new-v (assoc small-v 5 :x))
新旧向量共享大部分数据。
九、实践示例 #
9.1 矩阵操作 #
clojure
(def matrix
[[1 2 3]
[4 5 6]
[7 8 9]])
(defn get-cell [matrix row col]
(get-in matrix [row col]))
(defn set-cell [matrix row col value]
(assoc-in matrix [row col] value))
(get-cell matrix 1 2)
(set-cell matrix 1 2 :x)
9.2 队列实现 #
clojure
(defn make-queue []
[])
(defn enqueue [queue item]
(conj queue item))
(defn dequeue [queue]
{:item (first queue)
:queue (subvec queue 1)})
(-> []
(enqueue 1)
(enqueue 2)
(enqueue 3))
9.3 环形缓冲区 #
clojure
(defn make-ring-buffer [size]
{:buffer (vec (repeat size nil))
:size size
:head 0
:count 0})
(defn ring-push [{:keys [buffer size head count] :as rb} item]
(-> rb
(assoc :buffer (assoc buffer head item))
(update :head #(mod (inc %) size))
(update :count #(min (inc %) size))))
9.4 批处理 #
clojure
(defn process-batches [items batch-size f]
(mapcat f (partition-all batch-size items)))
(process-batches (range 10) 3
(fn [batch]
(map inc batch)))
十、向量字面量语法 #
10.1 类型提示向量 #
clojure
(def ^ints int-array (int-array [1 2 3]))
(vector-of :int 1 2 3 4 5)
(vector-of :double 1.0 2.0 3.0)
10.2 带元数据的向量 #
clojure
(def v ^:private [1 2 3])
(meta v)
十一、总结 #
向量操作速查:
| 操作 | 函数 |
|---|---|
| 创建 | [], vector, vec |
| 访问 | nth, get, (v index) |
| 添加 | conj, into |
| 修改 | assoc, update |
| 删除 | pop, subvec |
| 判断 | vector?, empty? |
关键点:
- 向量是最常用的数据结构
- 随机访问和尾部添加高效
- 使用
conj在尾部添加 subvec是O(1)操作- 持久化数据结构共享内存
下一步,让我们学习映射!
最后更新:2026-03-27