Map clojure的双向地图?

Map clojure的双向地图?,map,clojure,bidirectional,Map,Clojure,Bidirectional,在clojure中实现双向映射的最佳方式是什么?(所谓双向映射,我指的是一个可以同时提供A->B和B->A访问的关联映射。因此,实际上,值本身就是朝相反方向移动的键。) 我想我可以设置两张地图,每个方向一张,但是有没有更惯用的方法呢 我对需要双射(意味着没有两个键可以映射到同一个值)的情况和不施加该条件的情况都感兴趣。您可以始终使用Java库来实现这一点,如中的一个集合。TreeBidiMap实现了java.util.Map,因此它甚至可以不费吹灰之力地进行排序 user> (def x

在clojure中实现双向映射的最佳方式是什么?(所谓双向映射,我指的是一个可以同时提供A->B和B->A访问的关联映射。因此,实际上,值本身就是朝相反方向移动的键。)

我想我可以设置两张地图,每个方向一张,但是有没有更惯用的方法呢


我对需要双射(意味着没有两个键可以映射到同一个值)的情况和不施加该条件的情况都感兴趣。

您可以始终使用Java库来实现这一点,如中的一个集合。TreeBidiMap实现了
java.util.Map
,因此它甚至可以不费吹灰之力地进行排序

user> (def x (org.apache.commons.collections.bidimap.TreeBidiMap.))
#'user/x
user> (.put x :foo :bar)
nil 
user> (keys x)
(:foo)
user> (.getKey x :bar)
:foo
user> (:foo x)
:bar
user> (map (fn [[k v]] (str k ", " v)) x)
(":foo, :bar")
但是有些事情不会起作用,比如
assoc
dissoc
,因为它们需要持久的集合,而TreeBidiMap是可变的

如果您真的想在原生Clojure中这样做,可以使用元数据来保存反向散列。这仍然会使您的内存需求增加一倍,每次添加和删除的时间增加一倍,但查找速度足够快,至少所有内容都是捆绑的

(defn make-bidi []
  (with-meta {} {}))

(defn assoc-bidi [h k v]
  (vary-meta (assoc h k v)
             assoc v k))

(defn dissoc-bidi [h k]
  (let [v (h k)]
   (vary-meta (dissoc h k)
              dissoc v)))

(defn getkey [h v]
  ((meta h) v))
当然,您可能需要实现一系列其他功能才能获得完整的功能。不确定这种方法是否可行

user> (def x (assoc-bidi (make-bidi) :foo :bar))
#'user/x
user> (:foo x)
:bar
user> (getkey x :bar)
:foo

在大多数情况下,我发现以下方法很好:

(defn- bimap 
   [a-map]
   (merge a-map (clojure.set/map-invert a-map)))
这将简单地将原始贴图与反转贴图合并到新贴图中,然后可以对结果使用常规贴图操作


这很快又脏,但显然,如果任何键可能与任何值相同,或者
a-map
中的值不是唯一的,则应该避免此实现。

我们可以将此问题重新定义为:
给定一个元组列表
([a1b1][a2b2]…)
我们希望能够通过
a
b
快速查找元组。所以我们需要映射
a->[ab]
b->[ab]
。这意味着至少可以共享元组
[ab]
。如果我们考虑实现它,那么很快就会发现这是一个内存中的数据库表,其中的A列和B列都由这两列索引

因此,您可以只使用一个轻量级内存数据库


很抱歉,我对Java平台不够熟悉,无法推荐一些特定的东西。当然,我想到了原因,但对于这个特定的用例来说,这可能是一种过度的杀伤力。另一方面,它具有不变性,根据您对Brian答案的评论,您似乎希望它具有不变性。

谢谢,这很有帮助。我希望有一个clojure本地选项,所以你的第二个想法是我可以尝试的。