我不确定最高效的方法是什么,但这里有几个可能有用的建议:
问题文本的基本预期行为是不可能的:如果 a
和 b
是地图,使得 b
包含至少一个在 a
中不存在的键,则 (merge b <sth>)
不能等于 a
。
如果您最终选择使用interop解决方案,但随后需要返回到 PersistentHashMap
,则始终可以使用
(clojure.lang.PersistentHashMap/create
(doto (java.util.HashMap.)
(.put :foo 1)
(.put :bar 2)))
如果你需要将Clojure map的键集传递给Java方法,可以使用
(.keySet {:foo 1 :bar 2})
; => #< [:foo, :bar]>
如果涉及的所有键都保证是 Comparable
,这可以被利用来高效计算具有许多键的映射的 difference
(排序和合并扫描)。对于无限制的键,这当然是不可行的,并且对于小型映射,实际上可能会降低性能。
有一个用Clojure编写的版本是很好的,即使只是为了设置基准性能期望。 这是一个: (已更新)
(defn map-difference [m1 m2]
(loop [m (transient {})
ks (concat (keys m1) (keys m2))]
(if-let [k (first ks)]
(let [e1 (find m1 k)
e2 (find m2 k)]
(cond (and e1 e2 (not= (e1 1) (e2 1))) (recur (assoc! m k (e1 1)) (next ks))
(not e1) (recur (assoc! m k (e2 1)) (next ks))
(not e2) (recur (assoc! m k (e1 1)) (next ks))
:else (recur m (next ks))))
(persistent! m))))
我认为只需执行
(concat (keys m1) (keys m2))
,即使可能会重复一些工作,但大多数情况下比在每一步都检查给定的键是否也存在于“另一个映射”中更有效率。
为了总结答案,这里是一个基于集合的非常简单的版本,它有一个不错的特性,即它可以表达它的意思 - 如果我误解了规范,在这里应该很容易看出来。 :-)
(defn map-difference [m1 m2]
(let [ks1 (set (keys m1))
ks2 (set (keys m2))
ks1-ks2 (set/difference ks1 ks2)
ks2-ks1 (set/difference ks2 ks1)
ks1*ks2 (set/intersection ks1 ks2)]
(merge (select-keys m1 ks1-ks2)
(select-keys m2 ks2-ks1)
(select-keys m1
(remove (fn [k] (= (m1 k) (m2 k)))
ks1*ks2)))))
clojure.data.diff
在你的问题上表现如何? - Marko Topolnik