映射 #
一、映射简介 #
映射是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 |
关键点:
- 映射是最常用的数据结构之一
- 关键字作为键最常见
get-in/assoc-in/update-in处理嵌套- 解构语法简化访问
- 持久化保证不可变
下一步,让我们学习集合!
最后更新:2026-03-27