28

我有一个java.util.HashMap对象m(调用 Java 代码的返回值),我想获得一个带有附加键值对的新映射。

如果m是 Clojure 地图,我可以使用:

(assoc m "key" "value")

但是尝试一下HashMap会给出:

java.lang.ClassCastException:java.util.HashMap 无法转换为 clojure.lang.Associative

没有运气seq

(assoc (seq m) "key" "value")

java.lang.ClassCastException:clojure.lang.IteratorSeq 不能转换为 clojure.lang.Associative

我设法做到这一点的唯一方法是使用HashMap's own put,但这会返回void,所以我必须明确返回m

(do (. m put "key" "value") m)

这不是惯用的 Clojure 代码,而且我正在修改m而不是创建新地图。

如何以HashMap更接近 Clojure 的方式使用?

4

4 回答 4

35

Clojure 使 java Collections 可序列化,因此您可以直接在java.util.HashMap上使用 Clojure 序列函数。

但是assoc需要一个clojure.lang.Associative所以你必须首先将java.util.HashMap转换为:

(assoc (zipmap (.keySet m) (.values m)) "key" "value")

编辑:更简单的解决方案:

(assoc (into {} m) "key" "value")
于 2009-11-03T04:03:24.640 回答
29

如果您正在与 Java 代码交互,您可能不得不硬着头皮以 Java 的方式进行,使用.put. 这不一定是大罪;Clojure 为您提供了类似的东西do.特别是让您可以轻松地使用 Java 代码。

assoc仅适用于 Clojure 数据结构,因为已经进行了大量工作以非常便宜地创建它们的新(不可变)副本,只需稍作改动。Java HashMap 并非旨在以相同的方式工作。每次进行更改时,您都必须继续克隆它们,这可能会很昂贵。

如果您真的想摆脱 Java 变异领域(例如,也许您将这些 HashMaps 保留很长时间并且不希望 Java 调用到处都是,或者您需要通过printand序列化它们read,或者您想要使用 Clojure STM 以线程安全的方式使用它们)您可以轻松地在 Java HashMap 和 Clojure 哈希映射之间进行转换,因为 Clojure 数据结构实现了正确的 Java 接口,因此它们可以相互通信。

user> (java.util.HashMap. {:foo :bar})
#<HashMap {:foo=:bar}>

user> (into {} (java.util.HashMap. {:foo :bar}))
{:foo :bar}

如果你想要一个类似do- 的东西,一旦你完成了它就会返回你正在处理的对象,你可以使用doto. 事实上,Java HashMap 在官方文档中用作该函数的示例,这再次表明,如果您使用 Java 对象(明智地),这不是世界末日。

clojure.core/doto
([x & forms])
Macro
  Evaluates x then calls all of the methods and functions with the
  value of x supplied at the front of the given arguments.  The forms
  are evaluated in order.  Returns x.

  (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))

一些可能的策略:

  1. 如果可以的话,将你的突变和副作用限制在一个函数中。如果您的函数在给定相同输入的情况下始终返回相同的值,则它可以在内部做任何想做的事情。有时,改变数组或映射是实现算法的最有效或最简单的方法。只要您不向世界其他地方“泄漏”副作用,您仍然会享受函数式编程的好处。

  2. 如果您的对象将存在一段时间,或者它们需要与其他 Clojure 代码很好地配合,请尝试尽快将它们放入 Clojure 数据结构中,并在最后一秒将它们转换回 Java HashMaps(当输入他们回到Java)。

于 2009-11-03T08:51:51.407 回答
5

以传统方式使用 java hash map 是完全可以的。
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.

您正在修改一个真正打算修改的数据结构。Java 的哈希映射缺乏结构共享,无法有效地复制 Clojures 映射。通常惯用的方法是使用 java-interop 函数以典型的 java 方式处理 java 结构,或者将它们干净地转换为 Clojure 结构并以函数式 Clojure 方式使用它们。当然,除非它使生活更轻松并产生更好的代码;那么所有的赌注都被取消了。

于 2009-11-03T19:37:03.793 回答
2

这是我在尝试比较 clojure 版本与 java 的内存特性时使用 hashmaps 编写的一些代码(但从 clojure 使用)

(导入'(java.util Hashtable))
(定义频率2 [coll]
    (让[mydict(新哈希表)]
      (减少 (fn [计数 x]
            (让 [y (.toLowerCase x)]
              (如果(.get mydict y)
            (.put mydict y (+ (.get mydict y) 1))
            (.put mydict y 1)))) coll) mydict))

这是获取一些集合并返回每个不同事物(例如字符串中的一个词)被重用的次数。

于 2009-11-03T06:34:10.357 回答