Go语言学习之旅08——切片Slice

Go语言切片

切片可以传入三个参数,分别是[low, high, max],取元素就是从low开始,取high - low个数(等同于Java和Python),max - low的数作为新的切片的容量cap,可以不传max,当max不传时,容量默认和长度len相同。

即:新切片的len = high - low,cap = max - low

示例:

1
2
3
4
5
6
7
func main() {
arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println(arr[2:6]) // [2 3 4 5]
fmt.Println(arr[2:]) // [2 3 4 5 6 7]
fmt.Println(arr[:6]) // [0 1 2 3 4 5]
fmt.Println(arr[:]) // [0 1 2 3 4 5 6 7]
}

注意:切片包头不包尾

视图

由数组执行切片所返回的对象是一个view,即视图,若我们在视图上操作数组,会改变原数组

1
2
3
4
5
6
7
8
9
10
func updateSlice(arr []int) { // 接收一个切片的view作为参数,而不是不限制长度的数组
arr[0] = 99
}

func main() {
arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("前arr1 = ", arr1) // 前arr1 = [0 1 2 3 4 5 6 7]
updateSlice(arr1[:])
fmt.Println("后arr1 = ", arr1) // 后arr1 = [99 1 2 3 4 5 6 7]
}

补充:可以在view的基础上继续执行切片(slice),称为Reslice

slice的扩展

先看示例:

1
2
3
4
5
6
7
8
func main() {
arr1 := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("前arr1 = ", arr1) // [0 1 2 3 4 5 6 7]
var arr2 = arr1[3:6]
fmt.Println("第一次slice = ", arr2) // [3 4 5]
var arr3 = arr2[3:5]
fmt.Println("第二次slice = ", arr3) // [6 7]
}

对于切片,存在len()cap()两个概念,len范围内的元素是可以直接获取到的,而超出的部分如果依然在cap的返回内可以通过拓展获取,如果超出cap的长度,则会报错

注意:slice可以向后扩展,不可以向前扩展

slice的操作

添加元素

1
2
3
4
5
6
func main() {
s1 := []int{1, 2, 3} // 声明一个切片
var s2 = append(s1, 4)
fmt.Println("原切片s1 = ", s1) // 原切片s1 = [1 2 3]
fmt.Println("原切片s2 = ", s2) // 原切片s2 = [1 2 3 4]
}

补充:slice如果append的长度超过了cap,那么底层会重新给他分配一个更大的数组

补充:由于值传递的原因,必须接受append的返回值

slice的创建

slice的默认值是nil,len为0,cap为0,cap每一次扩充大小都是乘以2

1
2
3
4
5
6
7
8
9
10
func main() {
var s1 []int
s2 := []int{1, 2, 3} // 分配初值
s3 := make([]int, 4) // 不分配初始值,指定切片类型,指定长度。不指定容量cap时,容量和长度相同
s4 := make([]int, 4, 10) // 不分配初始值,指定切片类型,指定长度和容量cap
fmt.Println("s1 =", s1, "len =", len(s1), "cap =", cap(s1)) // s1 = [] len = 0 cap = 0
fmt.Println("s2 =", s2, "len =", len(s1), "cap =", cap(s2)) // s2 = [1 2 3] len = 3 cap = 3
fmt.Println("s3 =", s3, "len =", len(s1), "cap =", cap(s3)) // s3 = [0 0 0 0] len = 4 cap = 4
fmt.Println("s4 =", s4, "len =", len(s1), "cap =", cap(s4)) // s4 = [0 0 0 0] len = 4 cap = 10
}

slice的删除

删除某一下标的元素:

1
2
3
4
5
6
7
8
func main() {
s1 := []int{1, 2, 3, 4, 5}
fmt.Println("原分片s1 =", s1) // 原分片s1 = [1 2 3 4 5]

// 删除下标1的元素
s2 := append(s1[:1], s1[2:]...) // append可以接受可变参数,s1[2:]...语法可以分解分片为多个元素
fmt.Println("s1删除下标元素1后 =", s2) // s1删除下标元素1后 = [1 3 4 5]
}

删除分片的头元素:

1
2
3
4
5
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := s1[1:]
fmt.Println(s2) // [2 3 4 5]
}

删除分片的末元素:

1
2
3
4
5
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := s1[:len(s1)-1]
fmt.Println(s2) // [1 2 3 4]
}

slice的拷贝

copy()内建函数在第一个参数的基础上,将第二个参数的元素加在其前面,第一个元素被修改

1
2
3
4
5
6
7
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := []int{8, 9}
copy(s1, s2)
fmt.Println(s1) // [8 9 3 4 5]
fmt.Println(s2) // [8 9]
}

切片和数组的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
// 声明一个数组,数组的长度不可改变,len和cap永远都是5
arr := [5]int{1, 2, 3, 4, 5}
fmt.Printf("数组的长度:%d,数组的容量:%d\n", len(arr), cap(arr))

// 声明一个切片,切片的长度可变
slice := []int{}
fmt.Printf("原切片的长度:%d,数组容量:%d\n", len(slice), cap(slice))

// 给切片的末尾追加一个成员
slice = append(slice, 1)
fmt.Printf("append后切片的长度:%d,数组容量:%d\n", len(slice), cap(slice))
}

结果为:

1
2
3
数组的长度:5,数组的容量:5
原切片的长度:0,数组容量:0
append后切片的长度:1,数组容量:1