Clojure / Java的高效二进制序列化

时间:2011-10-09 05:28:36

标签: java serialization clojure

我正在寻找一种方法来有效地将Clojure对象序列化为二进制格式 - 即不仅仅是进行经典的打印和读取文本序列化。

即。我想做点什么:

(def orig-data {:name "Data Object" 
                :data (get-big-java-array) 
                :other (get-clojure-data-stuff)})

(def binary (serialize orig-data))

;; here "binary" is a raw binary form, e.g. a Java byte array
;; so it can be persisted in key/value store or sent over network etc.

;; now check it works!

(def new-data (deserialize binary))

(= new-data orig-data)
=> true

我的动机是我有一些包含大量二进制数据的大型数据结构(在Java数组中),我想避免将这些全部转换为文本并重新转换回来的开销。此外,我正在尝试保持格式紧凑,以最大限度地减少网络带宽使用。

我想要的具体功能:

  • 轻量级,纯Java实现
  • 支持所有Clojure的标准数据结构以及所有Java基元,数组等。
  • 无需额外的构建步骤/配置文件 - 我宁愿它只是“开箱即用”
  • 在处理所需的时间方面表现良好
  • 二进制编码表示的紧凑性

在Clojure中执行此操作的最佳/标准方法是什么?

4 个答案:

答案 0 :(得分:8)

我可能在这里遗漏了一些东西,但标准的Java序列化有什么问题?太慢,太大,别的什么?

用于普通Java序列化的Clojure包装器可能是这样的:

(defn serializable? [v]
  (instance? java.io.Serializable v))

(defn serialize 
  "Serializes value, returns a byte array"
  [v]
  (let [buff (java.io.ByteArrayOutputStream. 1024)]
    (with-open [dos (java.io.ObjectOutputStream. buff)]
      (.writeObject dos v))
    (.toByteArray buff)))

(defn deserialize 
  "Accepts a byte array, returns deserialized value"
  [bytes]
  (with-open [dis (java.io.ObjectInputStream.
                   (java.io.ByteArrayInputStream. bytes))]
    (.readObject dis)))

 user> (= (range 10) (deserialize (serialize (range 10))))
 true

有些值无法序列化,例如Java流和Clojure原子/代理/未来,但它应该适用于大多数普通值,包括Java原语和数组以及Clojure函数,集合和记录。

你是否实际保存任何东西取决于。在我对小数据集的有限测试中,序列化为文本和二进制似乎是大约相同的时间和空间。

但是对于大部分数据是Java基元数组的特殊情况,Java序列化可以快几个数量级并节省大量空间。 (在笔记本电脑上进行快速测试,100k随机字节:序列化0.9毫秒,100kB;文本490毫秒,700kB。)

请注意,(= new-data orig-data)测试对数组不起作用(它委托给Java的equals,对于数组只测试它是否是同一个对象),所以你可能需要/需要编写你的自己的相等函数来测试序列化。

user> (def a (range 10))
user> (= a (range 10))
true
user> (= (into-array a) (into-array a))
false
user> (.equals (into-array a) (into-array a))
false
user> (java.util.Arrays/equals (into-array a) (into-array a))
true

答案 1 :(得分:5)

Nippy是imho的最佳选择之一:https://github.com/ptaoussanis/nippy

答案 2 :(得分:4)

您是否考虑过Google的protobuf?您可能需要使用Clojure接口检查GitHub repository

答案 3 :(得分:3)

如果您没有提前架构,序列化为文本可能是您最好的选择。要一般地序列化任意数据,你需要做很多工作来保存对象图,并做反思以了解如何序列化所有内容......至少Clojure的打印机可以对{{进行静态的,无反射查找每个项目1}}。

相反,如果您真的想要优化的有线格式,则需要定义架构。我曾经使用过来自java的thrift和来自clojure的protobuf:既不是很有趣,但如果你事先计划的话,这并不是很麻烦。