[golang]slice的坑:从append到共享

栏目: Go · 发布时间: 6年前

内容简介:go是宣扬实用主义的语言,很多时候都把c中的最佳实践直接规定成语法了。其中之一就是slice,简单但是非常容易踩坑。先看一个小例子:在这个小例子中,原本是希望将

go是宣扬实用主义的语言,很多时候都把c中的最佳实践直接规定成语法了。其中之一就是slice,简单但是非常容易踩坑。

先看一个小例子:

func main() {
    a := make([]int, 2, 2)
    a[0], a[1] = 1, 2

    b := append(a[0:1], 3)
    c := append(a[1:2], 4)

    fmt.Println(b,c)
}

在这个小例子中,原本是希望将 a[0:1] 作为b的前缀,然后追加上3;将 a[1:2] 作为c的前缀,然后追加上4。但实际上输出结果并不是原本期望的 [1 3] [2 4] ,而变成了 [1 3] [3 4] 。这是为什么呢?

我们知道数据结构中数组是非常高效的,可以直接寻址,但是有个缺陷,难以扩容。所以slice被设计为指向数组的指针,在需要扩容时,会将底层数组上的值复制到一个更大的数组上然后指向这个新数组。

slice有个特性是允许多个slice指向同一个底层数组,这是一个有用的特性,在很多场景下都能通过这个特性实现 no copy 而提高效率。但共享同时意味着不安全。b在追加3时实际上覆盖了 a[1] ,导致c变成了 [3 4]

怎么解决呢?防止共享数据的出现问题需要注意两条,只读和复制,或者统一归纳为不可变。

写法1,make出一个新slice,然后先copy前缀到新数组上再追加:

func main() {
    a := make([]int, 2, 2)
    a[0], a[1] = 1, 2

    b := make([]int, 1)
    copy(b, a[0:1])
    b = append(b, 3)

    c := make([]int, 1)
    copy(c, a[1:2])
    c = append(c, 4)

    fmt.Println(b, c)
}

写法2,利用 go 中slice的一个小众语法, a[0:1:1] (源[起始index,终止index,cap终止index]) ,强迫追加时复制到新数组。

func main() {
    a := make([]int, 2, 2)
    a[0], a[1] = 1, 2

    b := append(a[0:1:1], 3)
    c := append(a[1:2:2], 4)

    fmt.Println(b, c)
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

疯传:让你的产品、思想、行为像病毒一样入侵(全新修订版)

疯传:让你的产品、思想、行为像病毒一样入侵(全新修订版)

[美] 乔纳•伯杰(Jonah Berger) / 乔迪、王晋 / 电子工业出版社 / 2016-6 / 68.00

是什么让事物变得流行? 从买轿车、买衣服、吃三明治,到给孩子取名字,你是否知道为什么某些产品会大卖,某些故事被人们口口相传,某些电子邮件更易被转发,或者某些视频链接被疯狂地点击,某些谣言更具传播力,某些思想和行为像病毒一样入侵你的大脑……这本书将为你揭示这些口口相传和社会传播背后的科学秘密,并且告诉你如何将产品、思想、行为设计成具有感染力和传播力的内容。 无论你是大公司的管理者,还是努......一起来看看 《疯传:让你的产品、思想、行为像病毒一样入侵(全新修订版)》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具