3

我尝试直接将 Clojure 的 hashmap 与 MapDB 一起使用,但遇到了奇怪的行为。我检查了 Clojure 和 MapDB 源,但无法理解问题所在。

首先一切看起来都很好:

lein try org.mapdb/mapdb "1.0.6"

; defining a db for the first time
(import [org.mapdb DB DBMaker])
(defonce db (-> (DBMaker/newFileDB (java.io.File. "/tmp/mapdb"))
                .closeOnJvmShutdown
                .compressionEnable
                .make))
(defonce fruits (.getTreeMap db "fruits-store"))
(do (.put fruits :banana {:qty 2}) (.commit db))

(get fruits :banana)
=> {:qty 2}
(:qty (get fruits :banana))
=> 2
(first (keys (get fruits :banana)))
=> :qty
(= :qty (first (keys (get fruits :banana))))
=> true

CTRL-D
=> Bye for now!

然后我尝试再次访问数据:

lein try org.mapdb/mapdb "1.0.6"

; loading previsously created db
(import [org.mapdb DB DBMaker])
(defonce db (-> (DBMaker/newFileDB (java.io.File. "/tmp/mapdb"))
                .closeOnJvmShutdown
                .compressionEnable
                .make))
(defonce fruits (.getTreeMap db "fruits-store"))

(get fruits :banana)
=> {:qty 2}
(:qty (get fruits :banana))
=> nil
(first (keys (get fruits :banana)))
=> :qty
(= :qty (first (keys (get fruits :banana))))
=> false
(class (first (keys (get fruits :banana))))
=> clojure.lang.Keyword

同一个关键字怎么会不同=呢?是否发生了一些奇怪的参考问题?

4

1 回答 1

3

问题是由关键字相等的工作方式引起的。=查看函数的实现,我们看到,因为关键字不是 clojure.lang.Number,或者clojure.lang.IPersistentCollection它们的相等性是根据Object.equals方法确定的。浏览源代码, clojure.lang.Keyword我们了解到关键字不会覆盖 ,因此如果两个关键字是相同的对象Object.equals,则它们是相等的。

MapDB 的默认序列化程序org.mapdb.SerializerPojoorg.mapdb.SerializerBase. 在其文档中,我们可以读到它是

使用“头字节”序列化/反序列化“java.lang”和“java.util”包中的大多数类的序列化器。

不幸的是,它不适用于clojure.lang类。它不保留关键字的身份,从而破坏了平等。为了修复它,让我们尝试使用 EDN 格式编写我们自己的序列化程序——或者,您可以考虑使用 Nippy——并在我们的MapDB中使用它。

(require '[clojure.edn :as edn])

(deftype EDNSeralizer []
  ;; See docs of org.mapdb.Serializer for semantics.
  org.mapdb.Serializer
  (fixedSize [_]
    -1)
  (serialize [_ out obj]
    (.writeUTF out (pr-str obj)))
  (deserialize [_ in available]
    (edn/read-string (.readUTF in)))
  ;; MapDB expects serializers to be serializable.
  java.io.Serializable)

(def edn-serializer (EDNSeralizer.))

(import [org.mapdb DB DBMaker])
(def db (.. (DBMaker/newFileDB (java.io.File. "/tmp/mapdb"))
            closeOnJvmShutdown
            compressionEnable
            make))

(def more-fruits (.. db
                     (createTreeMap "more-fruits")
                     (valueSerializer (EDNSeralizer.))
                     (makeOrGet)))
(.put more-fruits :banana {:qty 2})
(.commit db)

一旦在定义了定义的more-fruitsJVM 中重新打开树形图,存储在其中的对象将与任何其他 实例是相同的对象。因此,平等检查将正常工作。EDNSeralizer:qty:qty

于 2015-02-26T12:00:18.490 回答