Golang 中的数组 (array) 和切片 (slice)

中文描述约定

array: 数组

slice: 切片

来做个测试,不在机器上运行下面的代码,请给出输出

vals := make([]int, 5)
for i:= 0; i < 5; i++ {
    vals = append(vals, i)
}
fmt.Println(vals)

如果你猜测的结果是 [0 0 0 0 0 1 2 3 4],恭喜你答对了。为啥么正确答案不是 [0 1 2 3 4] 呢?大部分像我一样 Golang 初学者都会答错这道测试题,在这篇文章中,我们来探讨一下为什么不是我们期望的输出结果以及 slice 数据类型中 capacity 和 length 的用法。

Slices VS Arrays

初学 Golang 时可能会把数组和切片这两个概念搞混,但是如果习惯了使用切片,将会极大地优化代码;数组和切片有很多不同点,但在这里我们记住它们之间最大的区别就是:固定的数组大小是数组声明时的一部分,而切片可以有一个动态的大小。那么在实际的编码中是如何体现的呢?例如 val a [10]int, 这个数组有一个固定的大小 10 ,却不能改变长其度;如果我们调用 len(a),返回 10,因为长度就是数组的一部分;这就导致一个问题,如果有数组长度超过 10 的需求,你必须新建另外一个数组对象,譬如 val b [11]int, 并把数组 a 的值拷贝到数组 b ,在某种场景下固定长度的数组非常有用,但是通常我们长度灵活的数组,需要一种“没有固定大小的数组”,最直接的做法是声明数组时给足够大的长度,例如:

var vals [20]int
for i := 0; i < 5; i++ {
    vals[i] = i * i
}
subsetLen := 5

fmt.Println("The subset of our array has a length of:" subsetLen)

// Add a new item to our array
vals[subsetLen] = 123
subsetLen++
fmt.Print("The subset of our array has a length of:", subsetLen)

上面的例子中,我们声明数组时设置了其长度为 20 ,通过一个变量我们“设置”其长度为 5 ,我们往其加了一个元素之后, subset 自增 1,数组长度就变成了 6 。粗略地说,这就是切片的原理,通过设置一个值来限定数组的最大长度,在上述例子中为 20 ;同时切片还有一个值来表示数组元素的真实长度,在上述例子中为 subsetLen ;故切片中有 capacity cap 和 length len 两个概念。切片的内部实现基于数组,但是使用方式更灵活、运行更高效。cap 限定了切片中元素个数增加的上限,通过 append 函数,我们不需关心数组底层的个数上限。

回到我们的测试题

明白了数组和切片的概念和不同点之后,我们再次解释一下测试题。make 函数能够声明一个切片,第一个参数表示类型,第二个参数表示类型的长度,第三个参数为可选参数,表示类型的容纳上限;make([]int, 5) 表示我们要新建一个长度为 5 的切片,切片的默认容纳上限(capacity)和长度一致。但是 append 函数表示切片增加一个元素,那么该切片的容纳上限将同时增加,并在切片的末尾新增一个元素。也就是默认的切片值为 [0 0 0 0 0] 增加一个元素将会被放置在在第五个元素后面,所以运行结果如下:

vals := make([]int, 5)
fmt.Println("Capacity was:", cap(vals))
for i := 0; i < 5; i++ {
    vals = append(vals, i)
    fmt.Println("Capacity is now:", cap(vals))
}
fmt.Println(vals)
// output: [0 0 0 0 0 0 1 2 3 4]

如果修改为下面的代码,则输出为 [0, 1, 2, 3, 4]

vals := make([]int, 5)
for i := 0; i < 5; i++ {
      vals[i] = i
}
fmt.Println(vals)

代码不够简洁,改造一下:我们把 vals 的长度设置为 0 ,容纳上限为 5

vals := make([]int, 0, 5)
for i := 0; i < 5; i++ {
    vals = appen(vals, i)
}
fmt.Println(vals)

最后,如果能知道切片的上限,建议最好给 make 函数加上第三个参数。

0 条评论
您想说点什么吗?