Golang地图初始化

时间:2019-01-10 08:20:09

标签: dictionary go memory initialization slice

据我了解,Golang中的slicemap类型在很多方面都是相似的。它们都是reference(或container)类型。就抽象数据类型而言,它们分别表示一个数组和一个关联数组。

但是,他们的行为却大不相同。

var s []int
var m map[int]int

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

所以,问题是为什么在声明映射时为什么不能添加一些合成糖并且不能分配和初始化内存。

我确实用谷歌搜索了这个问题,学到了一个新词“ avtovivification”,但仍然没有找到原因。

添加:我不是在说结构文字。是的,您可以通过提供诸如m := map[int]int{1: 1}之类的值来显式初始化地图。但是,如果您有一些struct:

package main

import (
    "fmt"
)

type SomeStruct struct {
    someField map[int]int
    someField2 []int
}

func main() {
    s := SomeStruct{}
    s.someField2 = append(s.someField2, -1) // OK
    s.someField[0] = -1 // panic: assignment to entry in nil map
    fmt.Println(s)
}

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

2 个答案:

答案 0 :(得分:6)

  

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

那不是事实。切片和地图的默认值为zero value,更确切地说是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

Go Playground上尝试。

您对零值切片的“感觉”更多是可以在其中添加值。的确如此,但是在幕后,将使用确切的make()内置函数来分配新切片,您必须调用该内置函数才能为地图添加条目,并且必须(重新)分配返回的切片。因此,零值切片比零值映射“不再准备使用”。 append()仅负责必要的(重新)分配和复制。我们可以有一个“等效” addEntry()函数,您可以向其传递一个映射值和键值对,如果传递的映射为nil,它可以分配一个新的映射值并返回它。如果不调用append(),则无法将值添加到nil切片中,就像无法将条目添加到nil映射中一样。

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

是的,在某些情况下,您确实想立即使用切片和地图,在这种情况下,您可以自己叫make()或使用composite literal。您还可以使用特殊形式的make()来提供地图的(初始)容量,从而避免将来对地图内部结构进行重组(通常需要进行不可忽略的计算)。自动非nil默认值无法猜测您需要的容量。

答案 1 :(得分:1)

可以!您正在寻找的是:

package main

import "fmt"

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

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

    fmt.Println(v)
}

:=是声明分配的,其中var是简单声明的。