Вопрос

у меня есть java.util.HashMap объект m (возвращаемое значение вызова кода Java), и я хотел бы получить новую карту с дополнительной парой ключ-значение.

Если m если бы это была карта Clojure, я мог бы использовать:

(assoc m "key" "value")

Но попробовав это на HashMap дает:

java.lang.ClassCastException:java.util.HashMap нельзя привести к 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, плюс я модифицирую m вместо создания новой карты.

Как работать с HashMap в более стиле Clojure?

Это было полезно?

Решение

Clojure делает коллекции Java секвенируемыми, поэтому вы можете напрямую использовать функции последовательности Clojure в java.util.HashMap.

Но доцент ожидает Clojure.lang.Ассоциативный поэтому вам придется сначала преобразовать java.util.HashMap к тому, что:

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

Редактировать:более простое решение:

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

Другие советы

Если вы взаимодействуете с кодом Java, возможно, вам придется стиснуть зубы и сделать это способом Java, используя .put.Это не обязательно смертный грех;Clojure дает вам такие вещи, как do и . в частности, чтобы вы могли легко работать с кодом Java.

assoc работает только со структурами данных Clojure, потому что много работы было потрачено на то, чтобы сделать создание новых (неизменяемых) их копий с небольшими изменениями очень дешевым.Java HashMaps не предназначен для работы таким же образом.Вам придется клонировать их каждый раз, когда вы вносите изменения, что может быть дорогостоящим.

Если вы действительно хотите выбраться из страны мутаций Java (например,возможно, вы храните эти HashMaps в течение длительного времени и не хотите, чтобы Java-вызовы были повсюду, или вам нужно сериализовать их через print и read, или вы хотите работать с ними потокобезопасным способом, используя 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 традиционным способом.
(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.Если, конечно, это не облегчит жизнь и не приведет к улучшению кода;тогда все ставки сняты.

Это код, который я написал с использованием хэш-карт, когда пытался сравнить характеристики памяти версии Clojure и версии Java (но использовалось из 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))

Это значит взять некоторую коллекцию и вернуть, сколько раз каждая отдельная вещь (скажем, слово в строке) используется повторно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top