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

fox*_*nut 27 interop clojure hashmap purely-functional clojure-java-interop

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

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

(assoc m "key" "value")
Run Code Online (Sandbox Code Playgroud)

但试着这样做HashMap:

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

没有运气seq:

(assoc (seq m) "key" "value")
Run Code Online (Sandbox Code Playgroud)

java.lang.ClassCastException:clojure.lang.IteratorSeq无法强制转换为clojure.lang.Associative

我设法做到的唯一方法是使用HashMap自己的put,但返回void所以我必须明确返回m:

(do (. m put "key" "value") m)
Run Code Online (Sandbox Code Playgroud)

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

如何HashMap使用更多Clojure-ish方式?

Sid*_*ddy 33

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

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

(assoc (zipmap (.keySet m) (.values m)) "key" "value")
Run Code Online (Sandbox Code Playgroud)

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

(assoc (into {} m) "key" "value")
Run Code Online (Sandbox Code Playgroud)

  • 但是你没有一个hashmap,你有一个clojure地图,这似乎击败了他正在射击的点. (2认同)
  • 我相信如果你再次需要一个HashMap,你可以创建一个新的.(java.util.HashMap.m) (2认同)

Bri*_*per 24

如果您正在使用Java代码,那么您可能需要使用Java方式来执行它.put.这不一定是致命的罪; Clojure为您提供了类似的功能do,.因此您可以轻松地使用Java代码.

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

如果你真的想摆脱的Java突变的土地(例如,也许你周围保持这些包含HashMap很长一段时间,不希望Java中所有的地方调用,或者你需要通过它们序列化printread,或者你想为了使用Clojure STM以线程安全的方式使用它们,您可以轻松地在Java HashMaps和Clojure哈希映射之间进行转换,因为Clojure数据结构实现了正确的Java接口,因此它们可以相互通信.

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

user> (into {} (java.util.HashMap. {:foo :bar}))
{:foo :bar}
Run Code Online (Sandbox Code Playgroud)

如果你想要一个类似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))
Run Code Online (Sandbox Code Playgroud)

一些可能的策略:

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

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


Art*_*ldt 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方式使用它们.当然,除非它使生活更轻松并产生更好的代码; 然后所有的赌注都关闭了.