映射 #

一、映射简介 #

映射是Clojure中用于存储键值对的数据结构,类似于其他语言中的字典或哈希表。映射是Clojure中最常用的数据结构之一。

1.1 映射特点 #

  • 键值对存储
  • 键可以是任意类型
  • 键唯一,值可重复
  • 不可变、持久化
  • 高效查找O(log32 n)

1.2 创建映射 #

clojure
{:name "Alice" :age 30 :city "Beijing"}

{:a 1 :b 2 :c 3}

(hash-map :a 1 :b 2 :c 3)

(array-map :a 1 :b 2 :c 3)

(sorted-map :c 3 :a 1 :b 2)

(zipmap [:a :b :c] [1 2 3])

{}
clojure
(class {:a 1 :b 2})

1.3 映射类型 #

类型 特点 创建方式
HashMap 无序,高效 {}, hash-map
ArrayMap 有序,小规模 array-map
SortedMap 排序 sorted-map

二、基本操作 #

2.1 访问值 #

clojure
(def person {:name "Alice" :age 30 :city "Beijing"})

(:name person)

(:age person)

(:email person)

(:email person "N/A")

(get person :name)

(get person :email)

(get person :email "N/A")

(person :name)

(get person :name :not-found)

2.2 添加键值对 #

clojure
(def person {:name "Alice" :age 30})

(assoc person :city "Beijing")

(assoc person :email "alice@example.com" :phone "123-456")

(assoc person :age 31)

2.3 删除键值对 #

clojure
(def person {:name "Alice" :age 30 :city "Beijing"})

(dissoc person :city)

(dissoc person :city :age)

(dissoc person :email)

2.4 更新值 #

clojure
(def person {:name "Alice" :age 30})

(update person :age inc)

(update person :age + 5)

(update person :name str " Smith")

三、映射函数 #

3.1 判断函数 #

clojure
(map? {:a 1 :b 2})

(map? [:a :b])

(empty? {})

(empty? {:a 1})

(contains? {:a 1 :b 2} :a)

(contains? {:a 1 :b 2} :c)

(some? (:a {:a nil}))

(nil? (:b {:a 1}))

3.2 键和值 #

clojure
(def person {:name "Alice" :age 30 :city "Beijing"})

(keys person)

(vals person)

(set (keys person))

3.3 映射大小 #

clojure
(count {:a 1 :b 2 :c 3})

(count {})

3.4 合并映射 #

clojure
(merge {:a 1 :b 2} {:c 3 :d 4})

(merge {:a 1 :b 2} {:b 3 :c 4})

(merge-with + {:a 1 :b 2} {:a 3 :b 4 :c 5})

四、嵌套映射 #

4.1 创建嵌套映射 #

clojure
(def data
  {:user {:name "Alice"
          :profile {:email "alice@example.com"
                    :settings {:theme :dark
                               :notifications true}}}})

4.2 嵌套访问 #

clojure
(get-in data [:user :name])

(get-in data [:user :profile :email])

(get-in data [:user :profile :settings :theme])

(get-in data [:user :profile :address] "N/A")

4.3 嵌套更新 #

clojure
(assoc-in data [:user :profile :settings :theme] :light)

(update-in data [:user :profile :settings :notifications] not)

(update-in data [:user :name] str " Smith")

4.4 嵌套删除 #

clojure
(update-in data [:user :profile] dissoc :email)

五、映射遍历 #

5.1 map #

clojure
(map (fn [[k v]] [k (inc v)]) {:a 1 :b 2 :c 3})

(map-keys str {:a 1 :b 2})

(map-vals inc {:a 1 :b 2 :c 3})

5.2 reduce-kv #

clojure
(reduce-kv (fn [m k v]
             (assoc m k (* v 2)))
           {}
           {:a 1 :b 2 :c 3})

5.3 for #

clojure
(for [[k v] {:a 1 :b 2 :c 3}]
  [k (* v 2)])

(into {}
  (for [[k v] {:a 1 :b 2 :c 3}
        :when (even? v)]
    [k v]))

5.4 doseq #

clojure
(doseq [[k v] {:a 1 :b 2 :c 3}]
  (println k "->" v))

六、映射解构 #

6.1 基本解构 #

clojure
(let [{:keys [name age]} {:name "Alice" :age 30}]
  [name age])

(let [{:keys [name age city] :or {city "Unknown"}} {:name "Alice" :age 30}]
  [name age city])

(let [{:strs [name age]} {"name" "Alice" "age" 30}]
  [name age])

(let [{:syms [name age]} {'name "Alice" 'age 30}]
  [name age])

6.2 保留原始映射 #

clojure
(let [{:keys [name age] :as person} {:name "Alice" :age 30 :city "Beijing"}]
  [name age person])

6.3 函数参数解构 #

clojure
(defn greet [{:keys [name age]}]
  (str "Hello, " name "! You are " age " years old."))

(greet {:name "Alice" :age 30})

七、特殊映射类型 #

7.1 SortedMap #

clojure
(def sm (sorted-map :c 3 :a 1 :b 2))

(keys sm)

(subseq sm > :a)

(rsubseq sm < :c)

7.2 ArrayMap #

clojure
(def am (array-map :a 1 :b 2 :c 3))

(keys am)

ArrayMap保持插入顺序,适合小规模映射。

7.3 Record #

clojure
(defrecord Person [name age])

(def alice (->Person "Alice" 30))

(:name alice)

(assoc alice :city "Beijing")

八、高级操作 #

8.1 select-keys #

clojure
(select-keys {:a 1 :b 2 :c 3 :d 4} [:a :c])

8.2 rename-keys #

clojure
(defn rename-keys [m key-map]
  (reduce-kv (fn [m old new]
               (if (contains? m old)
                 (-> m
                     (assoc new (get m old))
                     (dissoc old))
                 m))
             m
             key-map))

(rename-keys {:a 1 :b 2} {:a :alpha :b :beta})

8.3 map-keys / map-vals #

clojure
(defn map-keys [f m]
  (reduce-kv (fn [m k v]
               (assoc m (f k) v))
             {}
             m))

(defn map-vals [f m]
  (reduce-kv (fn [m k v]
               (assoc m k (f v)))
             {}
             m))

(map-keys name {:a 1 :b 2})

(map-vals inc {:a 1 :b 2})

8.4 deep-merge #

clojure
(defn deep-merge [a b]
  (merge-with
    (fn [x y]
      (if (and (map? x) (map? y))
        (deep-merge x y)
        y))
    a b))

(deep-merge {:a {:b 1 :c 2}}
            {:a {:c 3 :d 4}})

九、实践示例 #

9.1 配置管理 #

clojure
(def default-config
  {:database {:host "localhost"
              :port 5432
              :name "myapp"}
   :server {:port 8080
            :mode :dev}})

(defn with-overrides [config overrides]
  (deep-merge config overrides))

(with-overrides default-config
  {:database {:host "production.db"}
   :server {:mode :prod}})

9.2 数据转换 #

clojure
(defn transform-user [user]
  (-> user
      (select-keys [:id :name :email])
      (update :name str/upper-case)
      (assoc :processed true)))

(transform-user {:id 1 :name "alice" :email "alice@example.com" :password "secret"})

9.3 统计计数 #

clojure
(defn word-count [words]
  (reduce (fn [counts word]
            (update counts word (fnil inc 0)))
          {}
          words))

(word-count ["apple" "banana" "apple" "cherry" "banana" "apple"])

9.4 分组数据 #

clojure
(defn group-by-key [coll key-fn]
  (reduce (fn [groups item]
            (update groups (key-fn item) (fnil conj []) item))
          {}
          coll))

(group-by-key [{:type :a :value 1}
               {:type :b :value 2}
               {:type :a :value 3}]
              :type)

十、总结 #

映射操作速查:

操作 函数
创建 {}, hash-map, zipmap
访问 (:key m), get, get-in
添加 assoc, assoc-in
删除 dissoc
更新 update, update-in
合并 merge, merge-with
判断 map?, contains?, empty?
键值 keys, vals

关键点:

  1. 映射是最常用的数据结构之一
  2. 关键字作为键最常见
  3. get-in/assoc-in/update-in 处理嵌套
  4. 解构语法简化访问
  5. 持久化保证不可变

下一步,让我们学习集合!

最后更新:2026-03-27