参数中使用的slice vs map [英] slice vs map to be used in parameter

查看:95
本文介绍了参数中使用的slice vs map的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In golang, slice and map are both reference types. When you simply need to modify elements in slice/map, the modification to slice/map members will be "broadcasted" to all slices. E.g, given m1 := make(map[int]int); m2 := m1, m1[3] = 5 will lead to m2[3] == 5.

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

However, when you try to add new elements into these two types, things start to differ. As is shown in the example below, the new elements added into a map param will be shown automatically in the argument; however, the new elements added into a slice, are 'discarded' in the argument.

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

Question is, why is this difference?

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]
}

我可能不清楚这个问题的意图,请让我改一下(对此表示抱歉,并感谢所有内部详细信息).

I may not be clear on the intention of this question and please let me rephrase it(sorry for this and thanks for all the internal details).

我真正想问的是,显然map是作为隐藏哈希表所有细节的指针实现的;为什么切片的实施方式不一样?

What I really want to ask is, apparently map is implemented as a pointer to hide all details for the hash map; why wasn't slice implemented similarly?

当前slice的实现确实是非常轻量级的,但是,从API的角度(像我们这样的golang用户和像Ross Cox这样的golang维护者之间的API)来看,这两种引用"类型的API并不是那么统一,并且可能为刚接触Golang的开发人员带来了陷阱.

The current slice implementation is indeed quite lightweight, however, from an API point of view (API between golang users like us and golang maintainers like Ross Cox), the APIs of these two 'reference' types are not that uniform and may lead to pitfalls for developers new to golang.

推荐答案

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

The difference in behavior lies in the implementation of those types.

映射是指向数据结构的指针,而切片是小的结构,其中包含指向后备数组的指针,切片的长度和容量. reflect.SliceHeader 为切片标头建模:

Maps are pointers to a data structure, while slices are small structs which contain the pointer to the backing array, the slice length and capacity. reflect.SliceHeader models a slice header:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

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

This is the main reason for the deviation: pointer vs struct.

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

When you add a new element to a map, the pointer remains the same. The pointed map structure may change, but the map pointer will not.

更改切片的 element 时,可以有效更改后备数组的元素.切片标头将保持不变:它将继续保持相同的指针,长度和容量. slice值(slice标头)的任何副本都指向同一个后备数组,因此,所有人都将看到更改的元素.

When you change an element of a slice, you effectively change the element of the backing array. The slice header will not change: it will continue to hold the same pointer, length and capacity. Any copiy of the slice value (the slice header) points to the same backing array, and as such, all will see the changed element.

将新元素添加到切片中时,描述 new 切片(包含附加元素)的切片标头必须更改:长度必须增加1,并且指针和容量也可能会发生变化(如果必须分配新的支持数组以容纳新元素).

When you add a new element to a slice, the slice header that describes the new slice (which contains the additional element) must change: the length must be increased by 1, and optionally the pointer and capacity may change too (if a new backing array had to be allocated to accommodate the new element).

一切都通过Go中的值传递.这是产生偏差的第二个原因.传递切片时,会从标头创建一个副本,并且如果将某些内容附加到副本中,即使将结果正确地分配给它(返回到副本),原始的切片标头也将一无所知.

Everything is passed by value in Go. This is the 2nd reason for the deviation. When a slice is passed, a copy is made from the header, and if something is appended to the copy, even if the result is properly assigned back to it (back to the copy), the original slice header will know nothing about that.

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

When a map is passed, the map value (which is a pointer) will also be copied, but both the original map pointer and the copy map pointer will point to the same map structure. Adding a value or changing the map via any of those pointers will change the one and only pointed map structure.

要使它们的行为相同,您必须使它们具有相同的"type" 类型,即:指针.如上所述,映射已经是(隐藏的)指针.如果继续进行操作并开始传递指向切片的指针,并且对 pointed 值进行操作,则它们的行为与映射相同.在实践中,这种方法很少使用(与切片指针相比,使用切片指针的语言支持要少得多),相反,另一种方法是广泛使用,返回新切片.您可以在此处了解更多信息:将切片指针切片为论证

To make them behave the same, you would have to make them the same "type" of type, that is: pointers. As noted above, maps are already (hidden) pointers. If you go ahead and start passing pointers to slices, and you operate on the pointed value, they will behave the same as maps. In practice this is rarely used (there is even less language support to aid working with slice pointers than array pointers), and rather the alternative approach is wide spread where the new slice is returned. You can read more about it here: Slicing a slice pointer passed as argument

这篇关于参数中使用的slice vs map的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆