wangpin34 / blog

个人博客, 博文写在 Issues 里
5 stars 0 forks source link

Go - array 和 slice #31

Open wangpin34 opened 5 years ago

wangpin34 commented 5 years ago

注:slice 中文翻译做切片,个人以为并不贴切,所以本文不使用切片称呼。

前言

编程语言的学习都绕不开数据类型这个话题,而数组作为支持最广泛的基础数据类型,其定义和使用在各个语言中基本大同小异。从这一点上说,编程语言互相借鉴(抄袭)一点都没有错。但之所以还会有这么多编程语言被发明和使用,是因为他们的应用场景或有所不同。比如偏重于组织复杂业务逻辑的,或者高并发性能的。

golang 中的数组的定义和使用都及其简单,但依然有他特别的地方,值得记录下来,以供查阅和分享。

数组初始化

定义数组只需要给出下面两个要素

  1. 元素类型
  2. 长度或者初始化元素 比如,创建一个长度为 2 的字符串数组,并赋值:
    arr1 := [2]string{}
    arr1[0] = "a"
    arr1[1] = "b"

    或者,初始化同时赋值

    arr1 := []string{"a", "b"}

    此时,长度值可以省略,数组长度以初始元素个数为准

操作数组

通过下标的方式访问数组元素,这个没什么可说的,java 如此,js 亦如此。

arr1[0]

更新:

arr1[0] = "a1"

数组的长度固定,所以,访问超出限制的下标将会导致 panic 程序中断。

index out of bounds

slice

slice 是对数组的一种高级抽象,底层存储依赖数组。实际项目中,slice 可以用作“可变长”数组。有两种定义 slice 的方法。

通过指定初始下标和结束下标(不包含)选取部分元素,如:

slice1 := arr1[0:1]

使用 make 函数,三个参数分别为初始化数组,长度,容量:

slice2 := make([]string{}, 2, 4)

可以看到,两种定义办法,都少不了一个底层数组。

操作 slice

访问和更新元素的方式和普通数组相同,所不同的是,slice 可以增加长度,这是个很有实际意义的特质。因为很多业务场景下,并不能确定元素的个数。

前面说到 slice 底层存储依然是数组,但数组是定长的,slice 是如何做到可变长的呢?通过下面的小实验可以稍微猜度一下。

playground

func print(s []string) {
  fmt.Printf("len(%d),cap(%d),elements:%s , %p \n", len(s), cap(s), s, s)
}

func main() {
  s := make([]string, 1, 2)
  print(s)
  s[0] = "a"
  print(s)
  s = append(s, "b")
  print(s)
  s = append(s, "c")
  print(s)
}

结果:

len(1),cap(2),elements:[] , 0x40c0e0 
len(1),cap(2),elements:[a] , 0x40c0e0 
len(2),cap(2),elements:[a b] , 0x40c0e0 
len(3),cap(4),elements:[a b c] , 0x442260 

当 cap 达到限制后,append 会返回一个新的 slice(注意新指针地址 0x442260),以及更大的 cap。所以,可以猜想,当 cap 发生变化时,Go 会创建一个新的数组,并将指针指向此数组。