Go的100天之旅-06数组和Slice
数组
Go
的数组和其它语言基本上一样,是长度固定的特定类型元素组成的序列,这基本上是所有语言数组的特性。和其它语言相比差异主要在声明和初始化的写法上,下面是简单声明一个数组:
var a [5]int
fmt.Println(a[0])
fmt.Println(fmt.Println(a[len(a)-1]))
上面的a
是一个长度为5
的整数数组,如果没有给定初始值它里面的元素默认值是0
。Go
数组的下标是从0开始的,len
函数返回数组中元素的个数。我们可以在声明数组的时候初始化它的值:
var m [3]int = [3]int{1, 2, 3}
var n [3]int = [3]int{2, 3}
这里的m
和n
都是长度为3
的数组,m
对应的值是1, 2, 3
而由于n
初始化的时候只有2
个值,因此它里面的值是2, 3, 0
。
如果采用简单声明:=
的方式声明一个数组,可以指定数组的大小,也可以不指定大小这时它会根据初始化值的个数来确定:
a := [10]int{} //元素都为0
b := [...]int{1, 2} //长度为2
Go
也可以直接指定一个索引和值,来初始化,如果声明的时候长度不指定,那最大的索引加1
就是数组的长度:
a := [10]int{1:2} // 长度10,a[1] = 2 其它为0
b := [...]int{1:2, 10:1} //长度11,a[1] = 2 a[10] = 1其它为0
数组a
长度声明是10
,只给了索引1
的值为2
,其余都为0
。数组b
声明的时候索引1
为2
,10
为1
,它的长度是11
修改数组中某个索引的值方式和其它语言一样:
a := [10]int{}
a[0] = 10
数组的类型是由元素的类型和长度共同决定的,[3]int
和[4]int
是两种不同的数组类型。因此:
a := [3]int{1, 2, 3}
a = [4]int{1, 2, 3, 4}
编译的时候会报错,而:
a := [3]int{1, 2, 3}
a = [3]int{4, 5, 6}
是正确的
Slice
上面说的数组长度是固定,使用的时候不是很灵活,slice
的长度是可变,简单声明一个未初始化的slice
var a []int
print(len(a))
print(cap(a))
slice
也可以利用len
返回它的长度,刚才声明的slice
长度为0
。除了长度slice
还有一个容量cap
的概念,用cap
可以返回它的容量。长度不能超过它的容量。slice的声明也可以用make
,用make
声明可以指定容量和可以不指定容量,这时容量和长度一致:
a := make([]int, 10) //长度为10,容量为10
a := make([]int, 10, 12) //长度为10 容量为12
slice
可以进行切片操作slice[i:j]
,创建一个新的slice
,新的slice
范围是原来slice
下标i
到j-1
,也可以不指定下标slice[:j]
则是默认从0
到j-1
,同理如果slice[i:]
就是从i
到最后一个元素,下面是一个简单的例子:
a := []int {1, 2, 3, 4, 5}
b = a[1:2]
c = a[:2]
d = a[1:]
需要注意的是切片操作slice[i:j]
,j
可以大于slice
的长度,只要它小于容量
刚才一直讲到容量,那它到底有什么实际意义呢?开始slice
的时候说过它长度是可变的,哪怎么改变了?通过append
就可以对slice
追加元素,这时容量就有作用了,如果append的时候当前长度小于容量,那slice
不会扩大容量,也不会申请新的空间,而是在原来的基础上把长度加1
就行了,如果容量等于长度,则会扩容,扩容会申请新的空间。
package main
func main() {
a := make([]int, 10, 11)
a = append(a, 1)
println(len(a), cap(a)) // 11 11
a = append(a, 1)
println(len(a), cap(a)) // 12 12
}