要在参数中使用的切片与映射

phy*_*hye 2 dictionary reference go slice

在golang中,切片和贴图都是引用类型.当您只需要修改切片/贴图中的元素时,切片/贴图成员的修改将"广播"到所有切片.例如,给定m1 := make(map[int]int); m2 := m1,m1[3] = 5将导致m2[3] == 5.

但是,当您尝试将新元素添加到这两种类型时,事情开始有所不同.如下例所示,添加到地图参数中的新元素将自动显示在参数中; 但是,添加到切片中的新元素在参数中被"丢弃".

问题是,为什么这有区别?

func editMap(m map[int]int) {
    m[3] = 8
    m[4] = 9
}

func editSlice(s []int) {
    s = append(s, 5)
    s = append(s, 9)
}

func main() {
    m := make(map[int]int, 0)
    m[1] = 5
    m[2] = 3
    fmt.Printf("%v\n", m)  //map[1:5 2:3]
    editMap(m)
    fmt.Printf("%v\n", m)  //map[1:5 2:3 3:8 4:9]

    s := make([]int, 2)
    s[0] = 2
    s[1] = 5
    fmt.Printf("%v\n", s)  //[2 5]
    editSlice(s)
    fmt.Printf("%v\n", s)  //[2 5]
}
Run Code Online (Sandbox Code Playgroud)

编辑:我可能不清楚这个问题的意图,请让我改一下(对不起,感谢所有的内部细节).

我真正想问的是,显然map是作为一个指针来实现的,它隐藏了哈希映射的所有细节; 为什么切片没有同样实施?

当前的切片实现确实相当轻量级,但是,从API的角度来看(像我们这样的golang用户和像Ross Cox这样的golang维护者之间的API),这两种"参考"类型的API并不统一,可能会导致陷阱适合初学者golang.

icz*_*cza 5

行为的差异在于这些类型的实现.

映射是指向数据结构的指针,而slice是小结构,包含指向后备阵列的指针,切片长度和容量.reflect.SliceHeader模型切片头:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}
Run Code Online (Sandbox Code Playgroud)

这是偏差的主要原因:指针与结构.

向地图添加新元素时,指针保持不变.指向的地图结构可能会改变,但地图指针不会改变.

更改切片的元素时,可以有效地更改支持数组的元素.切片头不会改变:它将继续保持相同的指针,长度和容量.切片值的任何复制(切片标题)指向相同的支持数组,因此,所有切片将看到更改的元素.

添加新元素的切片,切片标头,描述新的切片(其中包含附加元素)必须改变:长度必须增加1,和任选的指针和容量可能太改变(如果新的背衬必须分配数组以容纳新元素).

一切都在Go中传递.这是偏离的第二个原因.传递切片时,会从头部创建一个副本,如果将某些内容附加到副本上,即使将结果正确地分配给它(返回到副本),原始切片头也不会知道.

传递地图时,也会复制地图值(指针),但原始地图指针和复制地图指针都将指向相同的地图结构.通过任何这些指针添加值或更改地图将改变唯一的指向地图结构.

为了使它们表现相同,你必须使它们成为相同的"类型"类型,即:指针.如上所述,地图已经是(隐藏的)指针.如果继续并开始将指针传递给切片,并且您对指向的值进行操作,它们的行为将与地图相同.在实践中,这很少使用(有助于使用切片指针而不是数组指针的语言支持更少),而是替代方法在返回新切片的地方广泛传播.您可以在此处阅读更多相关信息:切片作为参数传递的切片指针