Question

J'ai un java.util.HashMap objet m (une valeur de retour à partir d'un appel à du code Java) et je voudrais obtenir une nouvelle carte avec un supplément de paire clé-valeur.

Si m ont été un Clojure carte, j'ai pu utiliser:

(assoc m "key" "value")

Mais essayer que sur un HashMap donne:

java.lang.ClassCastException:java.util.HashMap ne peut pas être lancé à clojure.lang.Associatif

Pas de chance avec seq soit:

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

java.lang.ClassCastException:clojure.lang.IteratorSeq ne peut pas être lancé à clojure.lang.Associatif

La seule façon que j'ai réussi à faire c'était d'utiliser HashMap's propre put, mais qui renvoie void donc, je dois renvoyer explicitement m:

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

Ce n'est pas idiomatique Clojure code, en plus je suis la modification m au lieu de créer une nouvelle carte.

Comment travailler avec un HashMap en plus de Clojure-ish sorte?

Était-ce utile?

La solution

Clojure fait les collections java seq-mesure, vous pouvez directement utiliser les fonctions de séquence Clojure sur la java.util.HashMap .

assoc attend clojure.lang.Associative de sorte que vous devrez d'abord convertir le java.util.HashMap que:

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

Edit: solution plus simple:

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

Autres conseils

Si vous interfacer avec le code Java, vous pourriez avoir à mordre la balle et faire le chemin Java, en utilisant .put. Ce n'est pas nécessairement un péché mortel; Clojure vous donne des choses comme do et . spécifiquement afin que vous puissiez travailler avec le code Java facilement.

assoc ne fonctionne que sur les structures de données Clojure parce que beaucoup de travail a été fait pour rendre très pas cher pour créer de nouvelles copies (immuables) avec de légères modifications. Java HashMaps ne sont pas destinés à travailler de la même manière. Il faudrait garder les cloner chaque fois que vous faites un changement, ce qui peut être coûteux.

Si vous voulez vraiment sortir de la mutation des terres Java (par exemple, peut-être vous gardez ces HashMaps autour depuis longtemps et ne veulent pas Java appelle partout, ou vous devez sérialisation via print et read, ou si vous voulez travailler avec eux d'une manière thread-safe en utilisant la Clojure STM), vous pouvez convertir entre Java HashMaps et Clojure hachage cartes assez facilement, parce que les structures de données Clojure mettre en œuvre les bonnes interfaces Java afin qu'ils puissent communiquer entre eux .

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

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

Si vous voulez une chose semblable do qui retourne l'objet que vous travaillez sur une fois que vous avez fini de travailler là-dessus, vous pouvez utiliser doto. En fait, Java HashMap est utilisé comme exemple dans la documentation officielle de cette fonction, ce qui est une autre indication que ce n'est pas la fin du monde si vous utilisez des objets Java (judicieusement).

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

Quelques stratégies possibles:

  1. Limiter la mutation et les effets secondaires à une seule fonction si vous le pouvez. Si votre fonction retourne toujours la même valeur étant donné les mêmes entrées, il peut faire ce qu'il veut à l'intérieur. Parfois, un tableau ou muter carte est la façon la plus efficace ou plus facile à mettre en œuvre un algorithme. Vous aurez toujours profiter des avantages de la programmation fonctionnelle aussi longtemps que vous faites des effets secondaires non « fuite » vers le reste du monde.

  2. Si vos objets vont être autour pendant un certain temps ou dont ils ont besoin pour jouer bien avec tout autre code Clojure, essayez de les obtenir dans les structures de données Clojure dès que vous le pouvez, et les jeter de nouveau dans Java HashMaps à la dernière seconde (lorsque les renvoyer à Java).

C'est tout à fait OK pour utiliser le java de hachage de la carte de façon traditionnelle.
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.

Vous êtes en train de modifier une structure de données qui est vraiment destiné à être modifié. Java hachage carte manque structurel de partage qui permet Clojures de la carte pour être efficace copié.Généralement idiomatiques façon de le faire est d'utiliser java-interop fonctions pour travailler avec la java des structures dans le typique java cours, ou à proprement les convertir en Clojure structures et de travailler avec eux dans le fonctionnel Clojure façon.Sauf si bien sûr, il rend la vie plus facile et les résultats dans un code de meilleure qualité;alors tous les paris sont éteints.

Ceci est un code que j'ai écrit en utilisant hashmaps quand je tentais de comparer les caractéristiques de mémoire de la version clojure vs java de (mais utilisé de clojure)

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

est de prendre une collection et retourner combien de fois chaque autre chose (disons un mot dans une chaîne) est réutilisée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top