[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)
}

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

查看所有标签

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

重新定义管理

重新定义管理

[美]布赖恩·罗伯逊 / 中信出版社 / 2015-10-1 / 45

还没听说过合弄制?你一定会听说的。终于,迎来了一本合弄制创建者的著作,讲解了这一公司经营方式的革命性新系统及其实施方法。 今天的商界,情况瞬息万变。但在绝大多数组织中,最具资格响应变化的人们却几乎都没有权力去做出改变。相反,他们不得不遵守那些由领导们设立的亘古不变的战略,而且这些领导们仍然相信“预测和控制”才是有效管理的关键。 合弄制向你展示了怎样让组织中工作的每一个人都成为一名领导,......一起来看看 《重新定义管理》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具