数组
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在Go语言中很少直接使用数组。
var a [3]int
var b [3]int = [3]int{1, 2, 3}
func main() {
fmt.Println(a[0])
fmt.Println(b[1])
}
在数组字面值中, 如果在数组的长度位置出现的是“…”省略号, 则表示数组的长度是根据初始化值的个数来计算:
var d = [...]int{1, 2, 3, 4}
func main() {
c := [...]int{1, 2, 3}
fmt.Println(c[1])
}
数组的长度是数组类型的一个组成部分, 因此[3]int和[4]int是两种不同的数组类型。
指定一个索引和对应值列表的方式初始化:
type Currency int
const (
USD Currency = iota
ERU
RMB
)
var symbol = [...]string{USD: "$", ERU: "@", RMB: "¥"}
func main() {
fmt.Println(symbol[RMB])
}
没用到的索引可以省略, 和前面提到的规则一样, 未指定初始值的元素将用零值初始化。例如:
r := [...]int{99: 1000}
定义了一个含有100个元素的数组r, 最后一个元素被初始化为1000, 其它元素都是用0初始化。
数组比较
只有当两个数组的所有元素都是相等的时候数组才是相等的。
a := [2]int{1, 2}
b := [...]int{1, 2}
fmt.Println(a == b) // true
slice切片
Slice( 切片) 代表变长的序列, 序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像, 只是没有固定长度而已。
一个slice由三个部分构成: 指针、 长度和容量。指针指向第一个slice元素对应的底层数组元素的地址;长度对应slice中元素的数目; 容量一般是从slice的开始位置到底层数据的结尾位置;内置的len和cap函数分别返回slice的长度和容量。
多个slice之间可以共享底层的数据, 并且引用的数组部分区间可能重叠。下面两个slice都包含了六月份:
var a = [...]int{1, 2, 3, 4, 5, 6, 7}
func main() {
b := a[2:4]
fmt.Println(b) // [3 4]
}
因为slice值包含指向第一个slice元素的指针, 因此向函数传递slice将允许在函数内部修改底层数组的元素。
func reverse(s []int) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
func main() {
// 数组
a := [...]int{0, 1, 2, 3, 4, 5}
b := [...]int{0, 1, 2, 3}
// 反转切片
reverse(a[:])
fmt.Println(a) //[5 4 3 2 1 0]
reverse(b[:])
fmt.Println(b) //[3 2 1 0]
}
slice类型和数组类型的初始化语法差异:slice并没有指明序列的长度。
func main() {
s := []int{0, 1, 2, 3, 4, 5}
reverse(s)
fmt.Println(s) // "[2 3 4 5 0 1]
}
如果你需要测试一个slice是否是空的, 使用len(s) == 0来判断。
内置的make函数创建一个指定元素类型、 长度和容量的slice。 容量部分可以省略, 在这种情况下, 容量将等于长度。
make([]T, len)
make([]T, len, cap) // same as make([]T, cap)[:len]
在底层, make创建了一个匿名的数组变量, 然后返回一个slice;只有通过返回的slice才能引用底层匿名的数组变量。在第一种语句中, slice是整个数组的view。 在第二个语句中, slice只引用了底层数组的前len个元素。
append函数
append函数对于理解slice底层是如何工作的非常重要,下面是第一个版本的appendInt函数, 专门用于处理[]int类型的slice:
func appendInt(x []int, val int) []int {
var z []int
zlen := len(x) + 1
if zlen <= cap(x) {
z = x[:zlen]
} else {
zcap := zlen
if zcap < 2*len(x) {
zcap = 2*len(x)
}
z = make([]int, zlen, zcap)
copy(z, x)
}
z[len(x)] = y
return z
}
- 检测slice底层数组是否有足够的容量来保存新添加的元素,如果有足够空间的话, 直接扩展slice。
- 如果没有足够的增长空间的话, appendInt函数则会先分配一个足够大的slice空间z
- 将输入的x复制到新的空间z, 然后添加y元素
内置的append函数可能使用比appendInt更复杂的内存扩展策略。因此我们并不知道append调用是否导致了内存的重新分配,也不能确认新的slice和原始的slice是否引用的是相同的底层数组空间。
因此, 通常是将append返回的结果直接赋值给输入的slice变量。
runes = append(runes, r)