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

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

内容简介: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)
}

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

查看所有标签

猜你喜欢:

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

缺失的终结

缺失的终结

【美】克尔•哈里斯 / 艾博 / 中国人民大学出版社 / 2017-6-15 / 39.00元

《缺失的终结》是一本反思互联网时代对普通人日常生活影响的书。作者认为,我们都曾渴望有一段远离现实生活烦扰的“瓦尔登湖”时光,悠闲地沉入自己的内心世界,从飞速变化的漩涡中暂时“缺失”。然而,互联网生活让我们每时每刻被互联网化,手环监测着我们的心跳和睡眠,微信、脸书、推特让我们人在床上就与世界链接起来,跑步了吗?走路了吗?英语单词背了吗?早餐的卡路里是否超标?坐在办公室,无数亟待处理的信息狂风暴雨般地......一起来看看 《缺失的终结》 这本书的介绍吧!

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

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

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

HSV CMYK互换工具