将地图序列化为 gob 后 DeepEqual 不正确
DeepEqual incorrect after serializing map into gob
我在使用 reflect.DeepEqual 时遇到了一些奇怪的行为。我有一个 map[string][]string
类型的对象,其中一个键的值为空切片。当我用gob编码这个对象,然后解码成另一个map时,根据reflect.DeepEqual这两个map是不相等的(尽管内容是一样的)
package main
import (
"fmt"
"bytes"
"encoding/gob"
"reflect"
)
func main() {
m0 := make(map[string][]string)
m0["apple"] = []string{}
// Encode m0 to bytes
var network bytes.Buffer
enc := gob.NewEncoder(&network)
enc.Encode(m0)
// Decode bytes into a new map m2
dec := gob.NewDecoder(&network)
m2 := make(map[string][]string)
dec.Decode(&m2)
fmt.Printf("%t\n", reflect.DeepEqual(m0, m2)) // false
fmt.Printf("m0: %+v != m2: %+v\n", m0, m2) // they look equal to me!
}
输出:
false
m0: map[apple:[]] != m2: map[apple:[]]
来自后续实验的一些笔记:
如果我将 m0["apple"]
的值设为非空切片,例如 m0["apple"] = []string{"pear"}
,则 DeepEqual returns 为真。
如果我将该值保留为空切片,但我从头开始构建相同的映射而不是使用 gob,则 DeepEqual returns true:
m1 := make(map[string][]string)
m1["apple"] = []string{}
fmt.Printf("%t\n", reflect.DeepEqual(m0, m1)) // true!
所以 DeepEqual 如何处理空切片并不是严格意义上的问题;它和 gob 的序列化之间有些奇怪的相互作用。
这是因为你编码了一个空切片,并且在解码过程中 encoding/gob
package only allocates a slice if the one provided (the target to decode into) is not big enough to accomodate the encoded values. This is documented at: gob: Types and Values:
In general, if allocation is required, the decoder will allocate memory. If not, it will update the destination variables with values read from the stream.
由于编码了 0 个元素,并且 nil
切片完全能够容纳 0 个元素,因此不会分配任何切片。如果我们打印切片与 nil
:
的比较结果,我们可以验证这一点
fmt.Println(m0["apple"] == nil, m2["apple"] == nil)
上面的输出是(在Go Playground上试试):
true false
请注意,fmt
包以相同的方式打印 nil
切片值和空切片:与 []
一样,您不能依赖其输出来判断切片是否为 nil
与否。
并且 reflect.DeepEqual()
将 nil
切片和一个空但非 nil
切片不同(非深度相等):
Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.
我在使用 reflect.DeepEqual 时遇到了一些奇怪的行为。我有一个 map[string][]string
类型的对象,其中一个键的值为空切片。当我用gob编码这个对象,然后解码成另一个map时,根据reflect.DeepEqual这两个map是不相等的(尽管内容是一样的)
package main
import (
"fmt"
"bytes"
"encoding/gob"
"reflect"
)
func main() {
m0 := make(map[string][]string)
m0["apple"] = []string{}
// Encode m0 to bytes
var network bytes.Buffer
enc := gob.NewEncoder(&network)
enc.Encode(m0)
// Decode bytes into a new map m2
dec := gob.NewDecoder(&network)
m2 := make(map[string][]string)
dec.Decode(&m2)
fmt.Printf("%t\n", reflect.DeepEqual(m0, m2)) // false
fmt.Printf("m0: %+v != m2: %+v\n", m0, m2) // they look equal to me!
}
输出:
false
m0: map[apple:[]] != m2: map[apple:[]]
来自后续实验的一些笔记:
如果我将 m0["apple"]
的值设为非空切片,例如 m0["apple"] = []string{"pear"}
,则 DeepEqual returns 为真。
如果我将该值保留为空切片,但我从头开始构建相同的映射而不是使用 gob,则 DeepEqual returns true:
m1 := make(map[string][]string)
m1["apple"] = []string{}
fmt.Printf("%t\n", reflect.DeepEqual(m0, m1)) // true!
所以 DeepEqual 如何处理空切片并不是严格意义上的问题;它和 gob 的序列化之间有些奇怪的相互作用。
这是因为你编码了一个空切片,并且在解码过程中 encoding/gob
package only allocates a slice if the one provided (the target to decode into) is not big enough to accomodate the encoded values. This is documented at: gob: Types and Values:
In general, if allocation is required, the decoder will allocate memory. If not, it will update the destination variables with values read from the stream.
由于编码了 0 个元素,并且 nil
切片完全能够容纳 0 个元素,因此不会分配任何切片。如果我们打印切片与 nil
:
fmt.Println(m0["apple"] == nil, m2["apple"] == nil)
上面的输出是(在Go Playground上试试):
true false
请注意,fmt
包以相同的方式打印 nil
切片值和空切片:与 []
一样,您不能依赖其输出来判断切片是否为 nil
与否。
并且 reflect.DeepEqual()
将 nil
切片和一个空但非 nil
切片不同(非深度相等):
Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.