Dictionary Go中的映射初始化

Dictionary Go中的映射初始化,dictionary,go,memory,initialization,slice,Dictionary,Go,Memory,Initialization,Slice,据我所知,在围棋中,类型slice和map在许多方面是相似的。它们都是引用(或容器)类型。就抽象数据类型而言,它们分别表示数组和关联数组 然而,他们的行为却大不相同 var s []int var m map[int]int 虽然我们可以立即使用声明的切片(追加新项目或重新切片),但我们不能对新声明的映射执行任何操作。我们必须调用make函数并显式初始化映射。因此,如果某个结构包含一个映射,我们必须为该结构编写一个构造函数 因此,问题是为什么在声明映射时不可能添加一些语法糖并分配和初始化内存

据我所知,在围棋中,类型
slice
map
在许多方面是相似的。它们都是
引用
(或
容器
)类型。就抽象数据类型而言,它们分别表示数组和关联数组

然而,他们的行为却大不相同

var s []int
var m map[int]int
虽然我们可以立即使用声明的切片(追加新项目或重新切片),但我们不能对新声明的映射执行任何操作。我们必须调用
make
函数并显式初始化映射。因此,如果某个结构包含一个映射,我们必须为该结构编写一个构造函数

因此,问题是为什么在声明映射时不可能添加一些语法糖并分配和初始化内存

我用谷歌搜索了这个问题,学会了一个新词“avtovivification”,但仍然看不出原因

我不是在说struct literal。是的,您可以通过提供诸如
m:=map[int]int{1:1}
之类的值来显式初始化映射。但是,如果您有一些:


无法立即使用结构(所有字段都有默认值)。必须为
SomeStruct
创建一个构造函数,该构造函数必须显式初始化映射。

您可以!您需要的是:

package main

import "fmt"

func main() {
    v := map[int]int{}

    v[1] = 1
    v[2] = 2

    fmt.Println(v)
}
:=
是declare和assign,其中as
var
只是declare

虽然我们可以立即使用声明的切片(追加新项目或重新切片),但我们不能对新声明的映射执行任何操作。我们必须调用
make
函数并显式初始化映射。因此,如果某个结构包含一个映射,我们必须为该结构编写一个构造函数

那不是真的。切片和贴图的默认值(或者更精确地说)为
nil
。您可以对
nil
贴图执行与
nil
切片相同的操作。您可以检查
nil
映射的长度,您可以为
nil
映射编制索引(结果将是映射值类型的零值),例如,以下各项均有效:

var m map[int]int

fmt.Println(m == nil) // Prints true
fmt.Println(len(m))   // Prints 0
fmt.Println(m[2])     // Prints 0
试穿一下

关于零值片,您“感觉”更多的是您可以向其添加值。这是真的,但是在引擎盖下,将使用确切的内置函数分配一个新的切片,为了向其添加条目,您必须调用该函数,并且您必须(重新)分配返回的切片。因此,零值切片与零值映射“没有更多的可用性”。只负责必要的(重新)分配和复制。我们可以有一个“等价的”
addEntry()
函数,您可以将映射值和键值对传递给它,如果传递的映射是
nil
,它可以分配一个新的映射值并返回它。如果不调用
append()
,则无法向
nil
切片添加值,就像无法向
nil
映射添加条目一样

切片和贴图的零值是
nil
(而不是初始化切片或贴图)的主要原因是性能和效率。通常情况下,映射或切片值(变量或结构字段)永远不会被使用,或者不会立即被使用,因此,如果在声明时分配它们,那将浪费内存(和一些CPU)资源,更不用说它会给垃圾收集器提供更多的工作。另外,如果零值是一个初始化值,那么它通常是不够的(例如,0大小的片不能容纳任何元素),并且在向其添加新元素时,它通常会被丢弃(因此初始分配将完全是浪费)


是的,在某些情况下,您确实希望立即使用切片和贴图,在这种情况下,您可以自己调用
make()
或使用。您还可以使用特殊形式的
make()
,为映射提供(初始)容量,避免映射内部的未来重组(通常需要不可忽略的计算)。自动的非
nil
默认值无法猜测您需要什么容量。

已经有了“语法糖”,但它不是糖,而是简单的Go-object-literal语法:{}`。切片和贴图的行为完全相同。@Volker据我所知,它们的行为方式不同。你能看一下我添加到问题中的示例吗?它们的行为完全相同。如果您试图分配给nil片的任何元素,您也会感到恐慌。您认为nil切片和nil映射行为的不同之处在于append函数的一个特殊属性,它允许其第一个参数为nil。贴图没有附加,但这与nil切片和nil贴图的行为没有区别。如果您编写自己的appendMap函数,您也可以将nil映射视为特殊的。在声明期间初始化在实践中是不可撤销的。吃片。您的切片要多长时间?在声明时,您无法知道,因此它必须为0。容量应该有多大?您不知道,任何像17这样的默认值都会破坏使用正确容量初始化切片以避免重新分配的能力。如果您想要一个构造的非nil切片或映射,只需添加{}@Volker,但是这种行为与
m:=make(map[int]int)
有什么不同呢?据我所知,该地图还创建了一些非零默认容量。一如既往,感谢您的回答。也许我错了,但我认为当您声明变量
var m map[int]int
时,内存已经分配,但尚未初始化?@AyZu map值内部是指针(请参阅)。map声明声明声明内存只用于一个指针(它将不指向任何地方),但在调用
make()
或使用复合文字之前,不会再分配更多的内存。
var m map[int]int

fmt.Println(m == nil) // Prints true
fmt.Println(len(m))   // Prints 0
fmt.Println(m[2])     // Prints 0