Clojure:以惯用的Clojure方式使用java.util.HashMap

时间:2009-11-03 03:43:08

标签: clojure interop hashmap purely-functional clojure-java-interop

我有一个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而不是创建新地图。

如何以更加Clojure-ish的方式使用HashMap

4 个答案:

答案 0 :(得分:32)

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")

答案 1 :(得分:23)

如果您正在使用Java代码进行接口,则可能需要使用.put以Java方式进行操作。这不一定是致命的罪; Clojure专门为您提供do.之类的内容,因此您可以轻松使用Java代码。

assoc仅适用于Clojure数据结构,因为很多工作已经变得非常便宜,可以通过轻微的改动来创建它们的新(不可变)副本。 Java HashMaps无意以相同的方式工作。每次进行更改时都必须继续克隆它们,这可能很昂贵。

如果你真的想要摆脱Java变异(例如,你可能要保持这些HashMaps很长一段时间并且不希望Java调用到处,或者你需要通过{{序列化它们) 1}}和print,或者你想使用Clojure STM以线程安全的方式使用它们,你可以很容易地在Java HashMaps和Clojure哈希映射之间进行转换,因为Clojure数据结构实现了正确的Java接口让他们可以互相交谈。

read

如果你想要一个user> (java.util.HashMap. {:foo :bar}) #<HashMap {:foo=:bar}> user> (into {} (java.util.HashMap. {:foo :bar})) {:foo :bar} 之类的东西,一旦你完成它就会返回你正在处理的对象,你可以使用do。实际上,Java HashMap在此函数的官方文档中用作示例,这是另一个迹象,如果您使用Java对象(明智地)它不是世界末日。

doto

一些可能的策略:

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

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

答案 2 :(得分:5)

以传统方式使用java哈希映射是完全可以的 (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方式使用它们。当然,除非它使生活更轻松并产生更好的代码;然后所有的赌注都关闭了。

答案 3 :(得分:1)

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

(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))

这是为了获取一些集合并返回每个不同的东西(比如字符串中的单词)重复使用的次数。