要在参数中使用的切片与地图 [英] slice vs map to be used in parameter

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

问题描述

在golang中,slice和map都是引用类型.当您只需要修改 slice/map 中的元素时,对 slice/map 成员的修改将广播"到所有切片.例如,给定 m1 := make(map[int]int);m2 := m1, m1[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.

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

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
", m)  //map[1:5 2:3]
    editMap(m)
    fmt.Printf("%v
", m)  //map[1:5 2:3 3:8 4:9]

    s := make([]int, 2)
    s[0] = 2
    s[1] = 5
    fmt.Printf("%v
", s)  //[2 5]
    editSlice(s)
    fmt.Printf("%v
", 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?

目前的切片实现确实是相当轻量级的,但是,从 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
}

这就是产生偏差的主要原因:pointer vs struct.

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.

当您更改切片的元素时,您实际上更改了支持数组的元素.切片头不会改变:它将继续保持相同的指针、长度和容量.切片值(切片标头)的任何副本都指向同一个后备数组,因此,所有人都会看到更改的元素.

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 copy of the slice value (the slice header) points to the same backing array, and as such, all will see the changed element.

当您添加一个新元素到切片时,描述切片(包含附加元素)的切片头必须改变:长度必须增加 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.

传递map时,map值(也就是指针)也会被拷贝,但是原始map指针和拷贝map指针都指向同一个map结构.通过这些指针中的任何一个添加值或更改映射都将更改唯一指向的映射结构.

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.

要使它们的行为相同,您必须使它们具有相同的 类型" 类型,即:指针.如上所述,地图已经是(隐藏的)指针.如果您继续将指针传递给切片,并对 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

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

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