一个从`map[string][]string`返回的空列表有什么特殊之处?

huangapple go评论80阅读模式
英文:

What's special about an empty list returned from a map[string][]string

问题

这里有一个奇怪的情况,我不明白Go语言在做什么。我最终得到了一些代码,使用了尚未插入该键的映射的默认值。

package main

import (
	"fmt"
	"reflect"
)

func main() {
	mmm := map[string][]string{}
	nnn := map[string][]string {
		"a": {},
	}
	
	x := mmm["a"]
	y := nnn["a"]
	z := []string{}
	
	fmt.Println(reflect.DeepEqual(x,y))
	fmt.Println(reflect.DeepEqual(x,z))
	fmt.Println(reflect.DeepEqual(y,z))

	fmt.Printf("%T, %T, %T", x, y, z)
}

我得到了意外的输出

false
false
true
[]string, []string, []string

我本来期望的是全都是true。这是关于映射的默认值的特性导致的。

英文:

Here's a weird situation where I don't understand what Go is doing. I ended with some code that used the default value of map that hasn't had that key inserted into it yet.

package main

import (
	"fmt"
	"reflect"
)

func main() {
	mmm := map[string][]string{}
	nnn := map[string][]string {
		"a": {},
	}
	
	x := mmm["a"]
	y := nnn["a"]
	z := []string{}
	
	fmt.Println(reflect.DeepEqual(x,y))
	fmt.Println(reflect.DeepEqual(x,z))
	fmt.Println(reflect.DeepEqual(y,z))

	fmt.Printf("%T, %T, %T", x, y, z)
}

I get the unexpected output

false
false
true
[]string, []string, []string

where I would expect true across the board. What is it about the default value of a map that does this?

答案1

得分: 3

如果你添加另一行打印代码:

fmt.Printf("%#v, %#v, %#v", x, y, z)

一切都会变得清晰。它的输出结果是(在Go Playground上尝试一下):

[]string(nil), []string{}, []string{}

x 是一个 nil 切片,而 y 是一个非 nil 切片,长度为 0,就像 z 一样。

如果使用一个不在 map 中的键进行索引,它会返回 map 的值类型的零值。在你的情况下,值类型是 []string,它是一个切片类型,切片类型的零值是 nil

reflect.DeepEqual() 文档说明了 nil 切片和非 nil 切片是不相等的:

注意,非 nil 的空切片和 nil 切片(例如,[]byte{}[]byte(nil))不是深度相等的。

英文:

If you add another printing line:

fmt.Printf("%#v, %#v, %#v", x, y, z)

It'll all become clear. It outputs (try it on the Go Playground):

[]string(nil), []string{}, []string{}

x is a slice being nil, and y is a non-nil slice having 0 length, just like z.

Indexing a map with a key that's not in the map, it results in the zero value for the value type of the map. In your case the value type is []string, its a slice type, and zero value for slice types is nil.

And reflect.DeepEqual() documents that a nil slice and a non-nilslice are not equal:

> Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.

答案2

得分: 2

首先,只是一个小的修正,这种数据类型被称为“切片”,而不是“列表”。

当你查找一个不存在的映射键时,你会得到映射值类型的“零值”。对于切片来说,零值是一个nil切片。一个nil切片通常的行为很像一个空切片(例如,nil切片的长度为零),但它们并不被认为是相同的。在Go语言中,切片的相等性实际上是没有定义的,所以由reflect.DeepEqual来决定这里的相等性的含义。

你会在文档中看到这个说明来解释这种行为:
> 当以下所有条件都为真时,切片值是深度相等的:它们都是nil或都是非nil的,它们具有相同的长度,并且它们要么指向相同底层数组的相同初始条目(即,&x[0] == &y[0]),要么它们的对应元素(长度上限)是深度相等的。注意,非nil的空切片和nil切片(例如,[]byte{}[]byte(nil))不是深度相等的。

英文:

First, just a quick correction, this kind of data type is called a "slice", not a "list".

When you look-up a map key that doesn't exist, you get the "zero value" of the map's value type. In the case of slices, the zero-value is a nil slice. A nil slice generally behaves much like an empty slice (for example, the len of a nil slice is zero), but they are not considered to be the same. Equality is not actually defined for slices in Go, so it is up to reflect.DeepEqual to decide what equality means here.

You'll see this note in the documentation explaining the behaviour here:
> Slice values are deeply equal when all of the following are true: they are both nil or both non-nil, they have the same length, and either they point to the same initial entry of the same underlying array (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal. Note that a non-nil empty slice and a nil slice (for example, []byte{} and []byte(nil)) are not deeply equal.

huangapple
  • 本文由 发表于 2021年7月17日 04:43:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/68415051.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定