首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在参数中使用的切片与映射

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

Stack Overflow用户
提问于 2017-12-01 09:37:24
回答 3查看 2.2K关注 0票数 4

在golang中,片和map都是引用类型。当您只需要修改切片/映射中的元素时,对片/映射成员的修改将被“广播”到所有切片。考虑到m1 := make(map[int]int); m2 := m1m1[3] = 5会导致m2[3] == 5

但是,当您尝试向这两种类型中添加新元素时,情况就开始不同了。如下面的示例所示,添加到map param中的新元素将在参数中自动显示;但是,添加到片中的新元素将在参数中被“丢弃”。

问题是,为什么这是不同的?

代码语言:javascript
复制
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]
}

编辑:我可能不清楚这个问题的意图,请让我重新表述它(对不起,并感谢所有的内部细节)。

我真正想问的是,显然map是作为一个指针来实现的,用于隐藏散列映射的所有细节;为什么没有类似的实现呢?

但是,从API的角度(我们这样的golang用户之间的API和Ross Cox这样的golang维护者之间的API)来看,当前的切片实现确实是非常轻量级的,这两种“引用”类型的API并不统一,可能会给新来的开发人员带来陷阱。

EN

回答 3

Stack Overflow用户

发布于 2017-12-01 09:41:38

行为的区别在于这些类型的实现。

映射是指向数据结构的指针,而切片是包含指向支持数组的指针、切片长度和容量的小结构。reflect.SliceHeader对片头进行了建模:

代码语言:javascript
复制
type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

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

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

更改片的元素时,可以有效地更改支持数组的元素。切片头不会更改:它将继续持有相同的指针、长度和容量。任何切片值(切片头)的副本都指向相同的支持数组,因此,所有人都将看到已更改的元素。

当您向片中添加新元素时,描述新片(其中包含附加元素)的片头必须更改:长度必须增加1,指针和容量也可能发生变化(如果必须分配新的支持数组以容纳新元素)。

每件事都是以价值来传递的。这是第二个偏差的原因。当一个切片被传递时,一个副本是从头创建的,如果某个东西被附加到副本中,即使结果被正确地分配回它(返回到副本),原始的片头将不知道这一点。

在传递映射时,映射值(即指针)也将被复制,但原始映射指针和复制映射指针都将指向相同的映射结构。通过任何这些指针添加值或更改映射,都将更改唯一且仅指向的映射结构。

要使它们的行为相同,您必须使它们具有相同的类型,即:指针。如上所述,地图已经是(隐藏的)指针。如果您继续并开始将指针传递给片,并且对指向的值进行操作,它们的行为将与映射相同。在实践中,这很少被使用(帮助使用片指针的语言支持甚至少于数组指针),相反,替代方法是在返回新切片的地方广泛使用。你可以在这里读到更多关于它的信息:切片作为参数传递的切片指针

票数 8
EN

Stack Overflow用户

发布于 2017-12-01 10:39:36

要修改切片,只需编辑如下代码(https://play.golang.org/p/2SeP93itIL):

代码语言:javascript
复制
func editSlice(s *[]int) {
    *s = append(*s, 5)
    *s = append(*s, 9)
}

有效围棋中有一些解释

如果一个函数使用了一个切片参数,那么它将使变为切片的 elements,调用者就会看到它。

这就是为什么这个切片的修改参数对调用者是可见的,但是在您返回它或分配给现有变量之前,新的切片本身是不可见的。因为append返回一个新的切片

之后我们必须返回切片,因为尽管追加可以修改片的元素,但本身是(保存指针、长度和容量的运行时数据结构)是通过值传递的。

票数 0
EN

Stack Overflow用户

发布于 2017-12-02 16:05:54

如果两个对象具有相同的内存地址,那么如果您更改其中一个的值,另一个就会更改,因为它们实际上是一个对象。

在您的代码中,meditMap中,mmain中具有相同的值:是同一个字典对象的地址。然而,seditSlice中的值和smain中的值是两个不同的对象。

我希望我的解释足够清楚。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47590444

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档