Golang Slice and Array
数组与切片
以前粗学时并没有留意 Golang 的数组,一直以为只有切片。
今天看《Go 语言实践》时才发现 Golang 也有数组。与切片的区别是定义时指定长度(例如长度为 4 的 int 型数组var array [4]int
)。
数组赋值时会拷贝整个数组,所以作为参数传递时也会完整拷贝,要考虑性能合理使用,必要时可以使用指针。
以下代码便于理解数组和切片:
package main
import "fmt"
func main() {
// 定义数组 a1
a1 := [4]int{1, 2, 3, 4}
// 数组 a2 来自 a1 的拷贝
a2 := a1
var a3 [4]int
a3 = a1 // 同样会完整拷贝
// a2 修改自己的内存区域
a2[0] = 0
// 切片 s1 来自 a1,其空间仍由 a1 实现
s1 := a1[1:2]
// 修改切片 s1 会影响数组 a1
s1 = append(s1, -1, -2)
fmt.Println("a1:", a1)
fmt.Println("a2:", a2)
fmt.Println("a3:", a3)
fmt.Println("s1:", s1)
}
输出:
a1: [1 2 -1 -2]
a2: [0 2 3 4]
a3: [1 2 3 4]
s1: [2 -1 -2]
Slice 保存的是长度、容量以及其底层数组的地址。
这里 s1 来自 a1 的切片,底层数组与 a1 同内存区域,所以对 s1 的 append 导致 a1 的数值被修改。
在使用中也要注意这种情况,合理使用。
使用三个索引创建切片
这种格式为:slice[i:j:k]
的切片方式,前两个索引与原来相同,第三索引用于表示新切片的容量。但它不是直接表示容量大小,而是表示原切片或者数组的索引位置。当它越界时会出现运行时错误。
所以新切片的长度和容量应该这样计算:
长度:j - i
容量:k - i
func main() {
a1 := [4]int{1, 2, 3, 4}
// s1 来自 a1
s1 := a1[1:2:3]
// 修改a[1]
a1[1] = 0
fmt.Println("a1:", a1)
fmt.Println("s1:", s1)
fmt.Println("-------------")
// append s1
s1 = append(s1, -1)
s1 = append(s1, -2)
fmt.Println("a1:", a1)
fmt.Println("s1:", s1)
}
输出:
a1: [1 0 3 4]
s1: [0] // <-对`a[1]`的修改也影响了 s1
---
a1: [1 0 -1 4]
s1: [0 -1 -2]
使用slice[i:j:k]
创建切片的时候,同样没有立即创建新的底层数组,只会在append
导致len > cap
扩容时才创建新数组。