25

我有一個java.util.HashMap對象m(從調用Java代碼的返回值),我想要一個新的地圖,值對。Clojure:在一個慣用的Clojure時尚中使用java.util.HashMap

如果m是一個Clojure的地圖,我可以使用:

(assoc m "key" "value") 

但是,試圖上一個HashMap給出:

java.lang.ClassCastException:java.util.HashMap中無法施展to 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代碼,加上我MODI與m搭檔,而不是創建新地圖。

如何以更Clojure的十歲上下的方式HashMap工作?

+2

我不認爲這是可能的。 Java的HashMap不是一個持久的數據結構,所以要麼修改它,要麼克隆它並修改克隆版本。 – copumpkin 2009-11-03 03:53:40

回答

30

Clojure使java集合可以被seq-able,所以你可以直接使用Clojure序列函數的java.util.HashMap

assoc命令需要一個clojure.lang.Associative所以你必須先將的java.util.HashMap轉換爲:

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

編輯:簡單的解決方案:

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

但是你沒有hashmap,你有一張clojure地圖,這看起來像是擊敗了他所射擊的點。 – Runevault 2009-11-03 06:30:21

+1

因此(assoc(到{} m)「key」「value」)而不是(assoc m「key」「value」)。美麗!謝謝。 – foxdonut 2009-11-03 11:34:41

+2

我相信如果你需要一個HashMap,你可以創建一個新的HashMap。 (java.util.HashMap中。米) – 2009-11-24 20:21:37

1

這是我編寫的一些代碼,當我試圖比較clojure版本與java的內存特性時使用hashmaps(但是使用cloj URE)

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

這是需要一定的收集和回多少次,每次不同的東西(比如字符串中的字)被再利用。

21

如果你正在使用Java代碼的接口,你可能必須硬着頭皮做Java的方式,使用.put。這不一定是一種致命的罪過; Clojure特別爲您提供了諸如do.之類的東西,因此您可以輕鬆使用Java代碼。

assoc只適用於Clojure數據結構,因爲大量的工作已經進入,使其創建新的(不可變的)副本非常便宜,只需稍作更改。Java HashMaps並不打算以相同的方式工作。每次進行更改時都必須克隆它們,這可能會很昂貴。

如果你真的想擺脫Java突變土地(例如,也許你保持這些哈希矩陣很長一段時間,並不希望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} 

如果你想有一個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時)。

+0

感謝您的詳細解釋。非常有用和有趣。我學習了關於「doto」而不是「do」的提示,這是一種在Java對象上調用void方法並在最後返回的更優雅方式。你會認爲我會意識到這一點,因爲我正在閱讀斯圖的書;-)在Clojure 1中btw – foxdonut 2009-11-03 11:36:26

+0

btw。7(2015),也許還有一些早期版本,java.util.HashMap現在甚至以Clojurely的方式打印:'(java.util.HashMap。{:a 1:b 2})'在回覆處打印作爲「{:a 1,:b 2}」,儘管它仍然是一個java.util.HashMap。例如'assoc'不會對它起作用。 – Mars 2015-12-07 05:33:14

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方式處理它們。除非它使生活變得更容易並且導致更好的代碼;那麼所有投注都關閉。