内置库来保存BTree,相当于Java的`FileChannel`和`ByteBuffer`

时间:2017-11-13 05:14:24

标签: go serialization persistence b-tree

所以,我按照CLRS算法用Java编写了这个持久的BTree。 我使用FileChannelByteBuffer将树存储在一个文件中,在需要时读取和写入节点。

我试着看看如何在Go中存储这样的BTree,并发现os.File,我认为它可以像Java的FileChannel一样使用。 但是,我找不到ByteBuffer的等价物。我查看了bytes.Buffer,我知道这是如何工作的,但它没有ByteBuffer方便的putIntputDouble等......

我是否必须自己实现那些将int和double转换为字节数组的函数?我也看了encoding.binary,但这看起来非常麻烦。我知道每次我必须将变量编码为一个字节数组,然后将该字节数组复制到缓冲区。

在这种情况下会推荐哪些结构?

2 个答案:

答案 0 :(得分:3)

使用encoding/gob

使用encoding/gob包,您可以将整个树序列化为一系列字节,并通过单个方法调用对它们进行反序列化。

见这个例子:

type Node struct {
    Data     int
    Children []*Node
}

func (n *Node) String() string {
    buf := &bytes.Buffer{}
    buf.WriteString(fmt.Sprintf("Node[Data: %d, Children: [", n.Data))
    for i, v := range n.Children {
        if i > 0 {
            buf.WriteString(", ")
        }
        buf.WriteString(v.String())
    }
    buf.WriteString("]")
    return buf.String()
}

不需要Node.String()方法,我只创建它以轻松打印/验证树。

现在使用gob序列化和反序列化树:

root := &Node{
    Data: 1,
    Children: []*Node{
        {Data: 2},
        {Data: 3},
    },
}
fmt.Println(root)

buf := &bytes.Buffer{}
if err := gob.NewEncoder(buf).Encode(root); err != nil {
    panic(err)
}

var root2 *Node
if err := gob.NewDecoder(buf).Decode(&root2); err != nil {
    panic(err)
}
fmt.Println(root2)

输出(在Go Playground上尝试):

Node[Data: 1, Children: [Node[Data: 2, Children: [], Node[Data: 3, Children: []]
Node[Data: 1, Children: [Node[Data: 2, Children: [], Node[Data: 3, Children: []]

这里我使用了内存缓冲区(bytes.Buffer),但如果你想从文件中保存到/加载,你甚至不需要内存缓冲区,你可以直接传递一个{ {3}}值*os.Filegob.NewEncoder()*os.File同时实现gob.NewDecoder()io.Reader)。

手动序列化/反序列化

另请注意,如果您不想(或不能)使用encoding/gob一步完成序列化,您还可以使用io.Writerbinary.Write()函数直接写入/读取文件而不使用任何内存缓冲区。

请参阅此示例对int32float64值进行编码和解码:

var i int32
var f float64

i, f = 1, 3.14

buf := &bytes.Buffer{}
if err := binary.Write(buf, binary.LittleEndian, i); err != nil {
    panic(err)
}
if err := binary.Write(buf, binary.LittleEndian, f); err != nil {
    panic(err)
}

var i2 int32
var f2 float64
if err := binary.Read(buf, binary.LittleEndian, &i2); err != nil {
    panic(err)
}
if err := binary.Read(buf, binary.LittleEndian, &f2); err != nil {
    panic(err)
}

fmt.Println(i2, f2)

输出(在binary.Read()上尝试):

1 3.14

同样,您可以将文件直接传递给binary.Read()binary.Write(),而不是*bytes.Buffer

非二进制序列化

您还可以使用其他非二进制序列化格式,例如JSON。  Go Playground包也可以通过一次调用序列化/反序列化整个树。尽管如此,使用JSON在存储和速度方面的性能较差,但序列化格式将更加人性化(更易读/可编辑),并与其他应用程序/技术兼容。

答案 1 :(得分:1)

请注意bytes.Buffer实现了io.Writer接口。因此,您可以使用fmt.Fprintf

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

format字符串参数可用于将整数和双精度直接写入缓冲区。有关详细信息,请参阅here