cafebabe go入门练习004:数组和切片
数组
数组是固定长度的特定类型元素的序列。数组的大小是数组定义的一部分,所以两个大小不同的数组是两个不同的类型,当然也不能比较。
数组的定义方式:
func main() {
var a [3]int
b := [3]int{1, 2, 3}
c := [...]int{1, 2, 3}
d := [...]int{1: 2, 0: 1, 2: 3}
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
go中函数传参一般都是值传递的,数组传参也是如此。如果确实要达到引用传递(pass arrays by reference)的效果,必须定义参数类型为指针。更好的方式是使用切片。
切片
go中数组使用很不灵活,因此需要一种灵活访问数组子序列的数据结构,这便是切片。
切片由三部分组成:
- data: 指针,切片起始位置指向的底层数组元素,并不一定是数组的第一个元素;
- length:切片大小
- capacity:切片容量,data指向的底层数组元素到最后一个元素。显然length<=capacity
切片和数组的关系如图:
所以,可以通过切片达到修改数组的目的,如反转数组元素:
// reverse reverses a slice of ints in place.
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() {
var a []int
fmt.Printf("a==nil:%t\tlen=%d\tcap=%d\n", a == nil, len(a), cap(a))
b := []int(nil)
fmt.Printf("b==nil:%t\tlen=%d\tcap=%d\n", b == nil, len(b), cap(b))
c := []int{}
fmt.Printf("c==nil:%t\tlen=%d\tcap=%d\n", c == nil, len(c), cap(c))
d := make([]int, 0, 0)
fmt.Printf("d==nil:%t\tlen=%d\tcap=%d\n", d == nil, len(d), cap(d))
}
输出如下:所以切片的零值是nil,但依然可以访问它,go确实更安全。
模拟append函数:
//append y to slice x
func appendInt(x []int, y int) []int {
var z []int
zlen := len(x) + 1
if zlen < cap(x) {
//空间够,z直接从x扩展来
z = x[:zlen]
} else {
//扩容,2倍速增长
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实现栈
stack = append(stack, v) // push v
top := stack[len(stack)-1] // top of stack
stack = stack[:len(stack)-1] // pop
slice删除中间位置
func remove(slice []int, i int) []int {
copy(slice[i:], slice[i+1:])
return slice[:len(slice)-1]
}