地图
与列表(顺序数据结构)和矢量(顺序和关联)不同,映射仅是关联数据结构。映射由一组从键到值的映射组成。所有键都是唯一的,因此 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
你可以分别使用 key
或 val
从地图条目中提取关键字或值 :
(key (find {:foo :bar} :foo))
;;=> :foo
(val (find {:foo :bar} :foo))
;;=> :bar
请注意,尽管所有 Clojure 映射条目都是向量,但并非所有向量都是映射条目。如果你尝试在任何不是地图条目的地方调用 key
或 val
,你将获得一个知识 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
获得的序列中的项目的顺序是未定义的。
你可以分别使用 keys
或 vals
获取一系列关键字或地图中的值 :
(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"})