地图

与列表(顺序数据结构)和矢量(顺序和关联)不同,映射仅是关联数据结构。映射由一组从键到值的映射组成。所有键都是唯一的,因此 map 支持从键到值的常量时间查找。

地图用花括号表示:

{}
;;=> {}

{:foo :bar}
;;=> {:foo :bar}

{:foo :bar :baz :qux}
;;=> {:foo :bar, :baz :qux}

每对两个元素是键值对。因此,例如,上面的第一张地图没有映射。第二个有一个映射,从关键:foo 到值:bar。第三个有两个映射,一个从关键:foo 到值:bar,一个从关键:baz 到值:qux。映射本质上是无序的,因此映射的显示顺序无关紧要:

(= {:foo :bar :baz :qux}
   {:baz :qux :foo :bar})
;;=> true

你可以使用 map? 谓词测试某些内容是否为地图 :

(map? {})
;;=> true

(map? {:foo :bar})
;;=> true

(map? {:foo :bar :baz :qux})
;;=> true

(map? nil)
;;=> false

(map? 42)
;;=> false

(map? :foo)
;;=> false

你可以使用 contains? 谓词测试地图是否在常量时间内包含给定

(contains? {:foo :bar :baz :qux} 42)
;;=> false

(contains? {:foo :bar :baz :qux} :foo)
;;=> true

(contains? {:foo :bar :baz :qux} :bar)
;;=> false

(contains? {:foo :bar :baz :qux} :baz)
;;=> true

(contains? {:foo :bar :baz :qux} :qux)
;;=> false

(contains? {:foo nil} :foo)
;;=> true

(contains? {:foo nil} :bar)
;;=> false

你可以使用 get 获取与密钥关联的值 :

(get {:foo :bar :baz :qux} 42)
;;=> nil

(get {:foo :bar :baz :qux} :foo)
;;=> :bar

(get {:foo :bar :baz :qux} :bar)
;;=> nil

(get {:foo :bar :baz :qux} :baz)
;;=> :qux

(get {:foo :bar :baz :qux} :qux)
;;=> nil

(get {:foo nil} :foo)
;;=> nil

(get {:foo nil} :bar)
;;=> nil

此外,映射本身是获取键并返回与该键关联的值的函数:

({:foo :bar :baz :qux} 42)
;;=> nil

({:foo :bar :baz :qux} :foo)
;;=> :bar

({:foo :bar :baz :qux} :bar)
;;=> nil

({:foo :bar :baz :qux} :baz)
;;=> :qux

({:foo :bar :baz :qux} :qux)
;;=> nil

({:foo nil} :foo)
;;=> nil

({:foo nil} :bar)
;;=> nil

你可以使用 find 将整个地图条目(键和值一起)作为双元素向量获取

(find {:foo :bar :baz :qux} 42)
;;=> nil

(find {:foo :bar :baz :qux} :foo)
;;=> [:foo :bar]

(find {:foo :bar :baz :qux} :bar)
;;=> nil

(find {:foo :bar :baz :qux} :baz)
;;=> [:baz :qux]

(find {:foo :bar :baz :qux} :qux)
;;=> nil

(find {:foo nil} :foo)
;;=> [:foo nil]

(find {:foo nil} :bar)
;;=> nil

你可以分别使用 keyval 从地图条目中提取关键字或值 :

(key (find {:foo :bar} :foo))
;;=> :foo

(val (find {:foo :bar} :foo))
;;=> :bar

请注意,尽管所有 Clojure 映射条目都是向量,但并非所有向量都是映射条目。如果你尝试在任何不是地图条目的地方调用 keyval,你将获得一个知识 15

(key [:foo :bar])
;; java.lang.ClassCastException:

(val [:foo :bar])
;; java.lang.ClassCastException:

你可以使用 map-entry? 谓词测试某些内容是否为地图条目 :

(map-entry? (find {:foo :bar} :foo))
;;=> true

(map-entry? [:foo :bar])
;;=> false

你可以使用 assoc 获取具有与现有地图相同的键值对的地图,并添加或更改一个映射:

(assoc {} :foo :bar)
;;=> {:foo :bar}

(assoc (assoc {} :foo :bar) :baz :qux)
;;=> {:foo :bar, :baz :qux}

(assoc {:baz :qux} :foo :bar)
;;=> {:baz :qux, :foo :bar}

(assoc {:foo :bar :baz :qux} :foo 42)
;;=> {:foo 42, :baz :qux}

(assoc {:foo :bar :baz :qux} :baz 42)
;;=> {:foo :bar, :baz 42}

你可以使用 dissoc 获取具有与现有地图相同的键值对的地图,可能删除了一个地图:

(dissoc {:foo :bar :baz :qux} 42)
;;=> {:foo :bar :baz :qux}

(dissoc {:foo :bar :baz :qux} :foo)
;;=> {:baz :qux}

(dissoc {:foo :bar :baz :qux} :bar)
;;=> {:foo :bar :baz :qux}

(dissoc {:foo :bar :baz :qux} :baz)
;;=> {:foo :bar}

(dissoc {:foo :bar :baz :qux} :qux)
;;=> {:foo :bar :baz :qux}

(dissoc {:foo nil} :foo)
;;=> {}

count 以恒定时间返回映射数:

(count {})
;;=> 0

(count (assoc {} :foo :bar))
;;=> 1

(count {:foo :bar :baz :qux})
;;=> 2

你可以使用 seq 获取地图中所有条目的序列:

(seq {})
;;=> nil

(seq {:foo :bar})
;;=> ([:foo :bar])

(seq {:foo :bar :baz :qux})
;;=> ([:foo :bar] [:baz :qux])

同样,地图是无序的,因此通过在地图上调用 seq 获得的序列中的项目的顺序是未定义的。

你可以分别使用 keysvals 获取一系列关键字或地图中的值 :

(keys {})
;;=> nil

(keys {:foo :bar})
;;=> (:foo)

(keys {:foo :bar :baz :qux})
;;=> (:foo :baz)

(vals {})
;;=> nil

(vals {:foo :bar})
;;=> (:bar)

(vals {:foo :bar :baz :qux})
;;=> (:bar :qux)

Clojure 1.9 添加了一个文字语法,用于更简洁地表示密钥共享相同名称空间的映射。请注意,两种情况下的映射都是相同的(映射不知道默认命名空间),这仅仅是语法上的便利。

;; typical map syntax
(def p {:person/first"Darth" :person/last "Vader" :person/email "darth@death.star"})

;; namespace map literal syntax
(def p #:person{:first "Darth" :last "Vader" :email "darth@death.star"})