我有一个 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自己的 put, ,但这会返回 void 所以我必须明确返回 m:

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

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

如何与 HashMap 以一种更像 Clojure 的方式?

有帮助吗?

解决方案

的Clojure使得Java集合SEQ-能,所以可以直接使用上的的java.util.HashMap Clojure的序列函数。

不过的 assoc命令的期望一个的 clojure.lang.Associative 的,所以你必须先转换的的java.util.HashMap 的到:

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

编辑:简单的解决方案:

(assoc (into {} m) "key" "value")

其他提示

如果您要与 Java 代码交互,您可能必须硬着头皮以 Java 方式进行操作,使用 .put. 。这不一定是死罪;Clojure 给你类似的东西 do. 特别是这样您就可以轻松地使用 Java 代码。

assoc 只适用于 Clojure 数据结构,因为我们已经做了很多工作,使得创建它们的新(不可变)副本只需稍加修改就变得非常便宜。Java HashMap 并不打算以同样的方式工作。每次进行更改时,您都必须不断克隆它们,这可能会很昂贵。

如果你真的想摆脱 Java 突变之地(例如也许您将这些 HashMap 保留很长时间并且不希望 Java 调用到处都是,或者您需要通过以下方式序列化它们 printread, ,或者您想使用 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 HashMap(当输入数据时)他们回到Java)。

按照传统方式使用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 方式处理它们。当然,除非它让生活变得更轻松并产生更好的代码;那么所有的赌注都失败了。

这是一些代码,我使用包含HashMap写到当我试图比较的版本的Clojure VS Java的存储特性(但是从Clojure的使用)

(import '(java.util Hashtable))
(defn frequencies2 [coll]
    (let [mydict (new Hashtable)]
      (reduce (fn [counts x]
            (let [y (.toLowerCase x)]
              (if (.get mydict y)
            (.put mydict y (+ (.get mydict y) 1))
            (.put mydict y 1)))) coll) mydict))

这是采取一些收集和回多少次,每次不同的东西(比如字符串中的字)被再利用。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top