克洛朱尔:以惯用的 Clojure 方式使用 java.util.HashMap
-
13-09-2019 - |
题
我有一个 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 调用到处都是,或者您需要通过以下方式序列化它们 print
和 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))
一些可能的策略:
如果可以的话,将突变和副作用限制在单个函数内。如果给定相同的输入,您的函数始终返回相同的值,那么它可以在内部执行任何它想要的操作。有时,改变数组或映射是实现算法的最有效或最简单的方法。只要您不向世界其他地方“泄漏”副作用,您仍然可以享受函数式编程的好处。
如果您的对象将存在一段时间或者它们需要与其他 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))
这是采取一些收集和回多少次,每次不同的东西(比如字符串中的字)被再利用。