Pergunta

Eu tenho um java.util.HashMap objeto m (um valor de retorno de uma chamada para o código Java) e gostaria de obter um novo mapa com um adicional de par chave-valor.

Se m foram um Clojure mapa, eu poderia usar:

(assoc m "key" "value")

Mas tentando que em um HashMap dá:

o java.lang.ClassCastException:o java.util.HashMap não pode ser convertido em clojure.lang.Associativa

Sem sorte com seq ou:

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

o java.lang.ClassCastException:clojure.lang.IteratorSeq não pode ser convertido em clojure.lang.Associativa

O único jeito que eu consegui fazer foi usar HashMap's própria put, mas que retorna void então, eu tenho explicitamente o retorno m:

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

Este não é idiomáticas Clojure código, mais eu estou modificando m em vez de criar um novo mapa.

Como trabalhar com um HashMap mais Clojure-ish forma?

Foi útil?

Solução

Clojure faz as Java Collections seq-capazes, para que você pode usar diretamente as funções de seqüência Clojure no java.util.HashMap .

e assoc espera um clojure.lang.Associative de forma que você tem que primeiro converter o java.util.HashMap para que:

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

Editar: solução mais simples:

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

Outras dicas

Se você está interagindo com código Java, você pode ter que morder a bala e fazê-lo da maneira Java, utilizando .put. Isso não é necessariamente um pecado mortal; Clojure dá-lhe coisas como do e . especificamente para que você possa trabalhar com código Java facilmente.

assoc só funciona em estruturas de dados Clojure, porque um monte de trabalho tem ido para fazer muito barato para criar novas cópias (imutáveis) deles com ligeiras alterações. Java HashMaps não se destinam a trabalhar da mesma maneira. Você teria que manter cloná-los cada vez que fizer uma alteração, que pode ser caro.

Se você realmente quiser sair de Java mutação do solo (por exemplo, talvez você está mantendo estas HashMaps em torno de um longo período de tempo e não querem Java chama em todo o lugar, ou você precisa serializar-los via print e read, ou você quer trabalhar com eles de uma forma thread-safe usando o Clojure STM) você pode converter entre Java HashMaps e Clojure hash mapas facilmente, porque estruturas de dados Clojure implementar as interfaces Java corretas para que eles possam conversar entre si .

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

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

Se você quiser um do-like coisa que retorna o objeto que você está trabalhando Assim que estiver pronto a trabalhar nele, você pode usar doto. Na verdade, um Java HashMap é usado como exemplo na documentação oficial para esta função, que é outra indicação de que não é o fim do mundo se você usar objetos Java (criteriosamente).

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

Algumas estratégias possíveis:

  1. Limite a sua mutação e efeitos colaterais para uma única função, se puder. Se a sua função sempre retorna o mesmo valor dado as mesmas entradas, ele pode fazer o que quer internamente. Às vezes, transformando um array ou mapa é a forma mais eficiente ou mais fácil de implementar um algoritmo. Você ainda vai desfrutar dos benefícios da programação funcional, desde que você não "vazar" efeitos colaterais para o resto do mundo.

  2. Se seus objetos vão ser em torno de um tempo ou eles precisam jogar bem com outro código Clojure, para tentar obter-los em estruturas de dados Clojure assim que você pode, e lançá-los de volta em Java HashMaps em o último segundo (quando alimentá-los de volta para Java).

É totalmente OK para usar o java mapa hash na forma tradicional.
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.

Você está a modificar uma estrutura de dados que realmente pretende ser modificado. Java hash do mapa de falta estrutural de partilha que permite Clojures do mapa para ser copiado de forma eficiente.Geralmente idiomáticas maneira de fazer isso é usar java-interoperabilidade funções para trabalhar com o java estruturas típicas de java, forma, ou de forma limpa convertê-los em Clojure estruturas e trabalhar com eles na funcionais Clojure forma.A menos, claro, torna a vida mais fácil e resulta em melhor de código;em seguida, todas as apostas estão fora.

Isto é algum código que eu escrevi usando HashMaps quando eu estava tentando comparar as características de memória da versão clojure vs java do (mas usado 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))

Esta é tomar alguma coleção e retornar quantas vezes cada coisa diferente (dizer uma palavra em uma string) é reutilizado.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top