Golang中的slice
1)基础
Slice更类似于"其他语言中的array",简单来说,它是一个指向一段数组的指针。
首先看看其声明:- var intSlice []int
另外这只是一个声明,所以intSlice会得到一个slice的默认值,即为nil:
- fmt.Printf("intSlice == nil? %v\n", intSlice == nil)
- 输出:
- intSlice == nil? true
- letsTry := intSlice
- fmt.Printf("intSlice == letsTry? %v\n", intSlice == letsTry)
- invalid operation: intSlice == letsTry (slice can only be compared to nil)
- intSlice = make([]int, 1, 3)
第一个参数是你要创建的东西的类型,这里要创建一个指向int数组的slice,即[]int;
第二个参数是该slice的长度,第三个参数是该slice的容量,这里分别是1和3;长度和容量分别代表什么,接下来我们会慢慢讲解。
我们先看一下我们刚才究竟创建了什么:
- fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))
- 输出:
- the intSlice is: [0], len: 1, cap: 3
所以,当我们尝试得到第二个元素,即intSlice[1]时:
- fmt.Printf("The intSlice[1] is: %d\n", intSlice[1])
- 报错:
- panic: runtime error: index out of range
- intSlice = intSlice[:len(intSlice)+1]
将新的slice赋给intSlice,现在我们看看其内容:
- fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))
- 输出
- the intSlice is: [0 0], len: 2, cap: 3
- intSlice = append(intSlice, 0)
- fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))
- 输出:
- the intSlice is: [0 0 0], len: 3, cap: 3
那这数组长度可以无限增加吗?我们先舍弃append,继续使用一开始的扩展方式:
- intSlice = intSlice[:len(intSlice)+1]
- fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))
- 报错:
- panic: runtime error: slice bounds out of range
在解决上面这个问题之前,先详细说明一下make([]int, 1, 3)究竟做了些什么。
2)make的细节
make([]int, 1, 3)究竟做了些什么?
首先,它在内存里分配了一段连续空间,这段空间的大小等于拥有3(cap)个元素的int数组(即[3]int{}),注意,只是空间大小相等;然后,它在这段连续空间的开始位置,创建一个只有1(len)个元素的int数组,即[1]int{};
最后,它创建并返回一个slice,这个slice包含3个信息,指向的元素类型及内存位置(这里是刚才[1]int{}的第一个元素)、len(长度,这里为1),cap(容量,这里为3)
3)append的细节
好的,我们回到1)中最后的问题:如果一个slice的长度已达到其容量,而我想继续扩展,该怎么办呢?很简单,建立一个更大连续空间,并把原本slice的内容复制进去。
而其实builtin里已存在方法能智能帮我们完成这动作:就是刚才的append()。- newIntSlice := append(intSlice, 0)
- fmt.Printf("the newIntSlice is: %v, len: %d, cap: %d \n", newIntSlice, len(newIntSlice), cap(newIntSlice))
- 输出:
- the newIntSlice is: [0 0 0 0], len: 4, cap: 6
那newIntSlice中的前三个元素是从intSlice复制过来的吗(而不是用slice指向)?我们验证一下:
- newIntSlice[0] = 1
- fmt.Printf("the newIntSlice is: %v, len: %d, cap: %d \n", newIntSlice, len(newIntSlice), cap(newIntSlice))
- fmt.Printf("the intSlice is: %v, len: %d, cap: %d \n", intSlice, len(intSlice), cap(intSlice))
- 输出:
- the newIntSlice is: [1 0 0 0], len: 4, cap: 6
- the intSlice is: [0 0 0], len: 3, cap: 3
当然intSlice与newIntSlice是相同类型的,可以直接用newIntSlice覆盖intSlice:
最后补充一下,创建slice的make方法可以只用两个参数,如make([]int, 3),这样得到的slice的len与cap都为3。
一下篇将会讨论slice的一些操作技巧