Frage

Ich habe ein java.util.HashMap Objekt m (ein Rückgabewert von einem Aufruf von Java-Code) und ich möchte eine neue Karte mit einem zusätzlichen Schlüssel-Wert-Paar erhalten.

Wenn m Wäre eine Clojure-Karte, könnte ich Folgendes verwenden:

(assoc m "key" "value")

Aber versuchen Sie es mal mit einem HashMap gibt:

java.lang.ClassCastException:java.util.HashMap kann nicht in clojure.lang.Associative umgewandelt werden

Kein Glück damit seq entweder:

(assoc (seq m) "key" "value")

java.lang.ClassCastException:clojure.lang.IteratorSeq kann nicht in clojure.lang.Associative umgewandelt werden

Der einzige Weg, den ich geschafft habe, war die Verwendung HashMapist sein eigenes put, aber das kommt zurück void also muss ich explizit zurückkehren m:

(do (. m put "key" "value") m)

Dies ist kein idiomatischer Clojure-Code, außerdem ändere ich ihn m anstatt eine neue Karte zu erstellen.

So arbeiten Sie mit einem HashMap auf eine Clojure-artigere Art und Weise?

War es hilfreich?

Lösung

Clojure macht die Java-Kollektionen Seq-fähig, so dass Sie direkt die Clojure Sequenzfunktionen auf dem java.util.HashMap verwenden können.

Aber Assoc erwartet ein clojure.lang.Associative so dass Sie müssen zuerst konvertieren die java.util.HashMap zu, dass:

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

Edit: einfachere Lösung:

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

Andere Tipps

Wenn Sie mit Java-Code sind eine Schnittstelle, können Sie die Kugel beißen und tun es die Java Art und Weise, .put verwenden. Dies ist nicht unbedingt eine Todsünde; Clojure gibt Ihnen Dinge wie do und . speziell so dass Sie leicht mit Java-Code arbeiten.

assoc funktioniert nur auf Clojure Datenstrukturen, weil viel Arbeit es sehr billig gegangen ist in die Herstellung neuer (unveränderlich) Kopien von ihnen mit leichten Veränderungen zu schaffen. Java HashMaps sind nicht dazu gedacht, auf die gleiche Art und Weise zu arbeiten. Sie müssten, um sie jedes Mal klonen Sie eine Änderung vornehmen, was teuer sein kann.

Wenn Sie wirklich wollen aus Java-Mutation-Land zu bekommen (zB vielleicht sind Sie diese HashMaps um für eine lange Zeit zu halten und nicht wollen, dass Java überall nennt, oder Sie brauchen, um sie über print serialisiert und read, oder Sie wollen mit ihnen in einem Thread-sichere Art und Weise mit der Clojure STM) Sie zwischen Java HashMaps und Clojure Hash-Karten leicht genug, denn Clojure Datenstrukturen implementieren die richtigen Java-Schnittstellen umwandeln kann arbeiten, damit sie miteinander reden können .

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

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

Wenn Sie eine do artige Sache wollen, die das Objekt zurückgibt Sie arbeiten, wenn Sie auf es getan arbeiten, können Sie doto verwenden. In der Tat ist ein Java HashMap wie das Beispiel in der offiziellen Dokumentation für diese Funktion verwendet, was ein weiterer Hinweis darauf, dass es nicht das Ende der Welt ist, wenn Sie Java-Objekte verwenden (umsichtig).

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

Einige mögliche Strategien:

  1. Beschränken Sie Ihre Mutation und Nebenwirkungen auf eine einzelne Funktion, wenn Sie können. Wenn Ihre Funktion immer den gleichen Wert zurückgibt die gleichen Eingänge gegeben, kann es tun, was es intern will. Manchmal mutiert ein Array oder Karte ist der effizienteste oder einfachste Weg, einen Algorithmus zu implementieren. Sie werden immer noch die Vorteile der funktionalen Programmierung genießen, solange Sie nicht „Leck“ Nebenwirkungen auf den Rest der Welt.

  2. Wenn Sie Ihre Objekte um für eine Weile sein werden oder sie müssen gut mit anderen Clojure Code spielen, versuchen, sie in Clojure Datenstrukturen so schnell wie möglich, und wirft sie in Java HashMaps zurück an die letzte Sekunde.

  3. (wenn sie zurück zu Java Fütterung)

Es ist völlig in Ordnung, die Java-Hash-Map auf herkömmliche Weise zu verwenden.
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.

Sie ändern eine Datenstruktur, die tatsächlich geändert werden soll. Der Hash-Map von Java fehlt die strukturelle gemeinsame Nutzung Dadurch können Clojures-Karten effizient kopiert werden.Der im Allgemeinen idiomatische Weg, dies zu tun, besteht darin, Java-Interop-Funktionen zu verwenden, um mit den Java-Strukturen auf die typische Java-Art zu arbeiten, oder sie sauber in Clojure-Strukturen umzuwandeln und mit ihnen auf die funktionale Clojure-Art zu arbeiten.Es sei denn natürlich, es macht das Leben einfacher und führt zu besserem Code.dann sind alle Wetten ungültig.

Dies ist ein Code I Hashmaps schrieb mit, als ich versuchte Speichereigenschaften der clojure Version vs Java (aber von clojure verwendet) zu vergleichen

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

Dies ist eine Sammlung zu nehmen und zurück, wie oft jede andere Sache (sagen wir ein Wort in einem String) wiederverwendet wird.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top