向量 #

一、向量简介 #

向量是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?

关键点:

  1. 向量是最常用的数据结构
  2. 随机访问和尾部添加高效
  3. 使用 conj 在尾部添加
  4. subvec 是O(1)操作
  5. 持久化数据结构共享内存

下一步,让我们学习映射!

最后更新:2026-03-27