25

私はjava.util.HashMapオブジェクトm(Javaコードの呼び出しからの戻り値)を持っています。値のペア。Clojure:慣用的なClojureファッションでjava.util.HashMapを使用する

mはClojureのマップだったら、私は使用することができます

(assoc m "key" "value") 

しかしHashMapにそれをしようとすることはできます:

とjava.lang.ClassCastException:java.util.HashMapをキャストすることはできませんが

01: seqのいずれかではありません運

clojure.lang.Associative

(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-ishの方法でHashMapを使用するにはどうすればよいですか?

+2

私はそれが可能ではないと思います。 JavaのHashMapは永続的なデータ構造ではないため、変更するか、複製して複製されたバージョンを変更してください。 – copumpkin

答えて

30

ClojureはJavaコレクションseq-ableを作成します。したがって、java.util.HashMapでClojureシーケンス関数を直接使用できます。

しかし連想はそうあなたが最初にそれにのjava.util.HashMapを変換する必要がありますclojure.lang.Associativeを期待:

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

編集:簡単なソリューション:

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

しかし、あなたはハッシュマップを持っていない、あなたは彼が撮影しているポイントを打ち負かすように思えるclojureマップを持っています。 – Runevault

+1

(assoc m "key" "value")の代わりに(assoc(into {} m) "key" "value")綺麗な!ありがとう。 – foxdonut

+2

もう一度HashMapが必要な場合は、新しいマップを作成することができます。 (のjava.util.HashMap。メートル) –

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コードとインターフェイスしている場合は、.putを使用して箇条書きを噛み込ませ、Javaの方法で行う必要があります。これは必ずしも死の罪ではありません。 Clojureは具体的にはdo.のようなものを提供するので、Javaコードを簡単に扱うことができます。

assocは、Clojureデータ構造でのみ機能します。これは、多少の変更を加えた新しい(変更不可能な)コピーを作成するための非常に安価な作業になっているためです。Javaハッシュマップは、同じ方法で動作するようには意図されていません。変更を加えるたびにクローニングを続ける必要がありますが、これは高価になる可能性があります。

あなたが本当にJavaの突然変異 - 土地から抜け出したいのであれば(例えば、あなたはこれらのHashMapを長い間持ち続けているかもしれませんし、Javaコールを全部したくない、あるいはprintおよびread)、Clojure STMを使用してスレッドセーフな方法でそれらを処理したい場合は、Clojureデータ構造が適切なJavaインターフェイスを実装しているため、Java HashMapsとClojureハッシュマップを簡単に変換できますその他。あなたは、あなたがそれに取り組んで完了したら、作業中のオブジェクトを返しますdo様なことをしたい場合は

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

user> (into {} (java.util.HashMap. {:foo :bar})) 
{:foo :bar} 

、あなたは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ハッシュマップに最後の2番目(Javaに戻すとき)。

+0

詳細な説明をありがとうございます。非常に有用で興味深い。私は "do"の代わりに "doto"のヒントを学びました。Javaオブジェクトのvoidメソッドを呼び出し、最後にそれらを取り戻すよりエレガントな方法を学びました。あなたは私がStuの本を読んでいるので、これを理解すると思うだろう;-) Clojure 1の – foxdonut

+0

btw。7(2015)、そして以前のバージョンでは、 'java.util.HashMap'がClojurelyの方法で出力されるようになりました。'(java.util.HashMap。{:a 1:b 2}) 'それはまだ '' java.util.HashMap' 'ですが、 "{:a 1、:b 2}"としています。例えば'assoc'は動作しません。 – Mars

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の方法でそれらを処理することです。もちろん、人生を楽にし、より良いコードを生み出すのでなければ、すべての賭けはオフです。

関連する問題