Go基础之切片Slice操作

向Slice中添加元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
arr := [...]string{"a", "b", "c", "d", "e", "f", "g", "h"}
s := arr[2:6]
ss := append(s, "xx")
sss := append(ss, "yy")
ssss := append(sss, "zz")
fmt.Println("s", s)
fmt.Println("ss", ss)
fmt.Println("sss", sss)
fmt.Println("ssss", ssss)
fmt.Println("arr", arr)
}

执行结果:

1
2
3
4
5
s [c d e f]
ss [c d e f xx]
sss [c d e f xx yy]
ssss [c d e f xx yy zz]
arr [a b c d e f xx yy]

从执行结果中可以看出, 向Slice中append元素后, 会覆盖掉底层Array的值, 会覆盖掉索引为ptr+len(Slice)的值. 当Slice append后, 超出了cap的长度, 底层Array中已经没有多余的值可以给他覆盖后, 这时, 新的Slice其实已经不再是老的Array的view了, Go语言会在内部创建一个新的Array, 把老数据拷贝过去, 且新的Array的长度会设置的更长一些. 上面的Demo中, ssss已经不再是原始Array的view

  • 当添加元素时, 如果超越了cap, 系统会重新分配更大的底层数组, 原来的Array如果没有被任何对象使用, 将被垃圾回收机制回收

  • 由于Go语言是值传递, 在append后, 必须接收其返回值. 原因是在append操作后, Slice的len肯定会变化, 如果超过了Array长度, cap的值也会变化, 所以需要我们使用新的Slice对象去接收新的返回值

创建Slice

上一篇文章的介绍中, 全篇都使用了Array去创建Slice, 我们可以直接创建Slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func printSliceInfo(s []int) {
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
}

func main() {
var s []int // 声明创建一个int类型的Slice
// 如果变量没有被赋值, Go语言会为每个变量定义Zero Value
// Slice的Zero Value 为 nil

for i:=0; i<=59; i++ {
printSliceInfo(s)
s = append(s, i)
}
fmt.Println(s)
}

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
len=0 cap=0
len=1 cap=1
len=2 cap=2
len=3 cap=4
len=4 cap=4
len=5 cap=8
len=6 cap=8
len=7 cap=8
len=8 cap=8
len=9 cap=16
len=10 cap=16
len=11 cap=16
len=12 cap=16
len=13 cap=16
len=14 cap=16
len=15 cap=16
len=16 cap=16
len=17 cap=32
len=18 cap=32
len=19 cap=32
len=20 cap=32
len=21 cap=32
len=22 cap=32
len=23 cap=32
len=24 cap=32
len=25 cap=32
len=26 cap=32
len=27 cap=32
len=28 cap=32
len=29 cap=32
len=30 cap=32
len=31 cap=32
len=32 cap=32
len=33 cap=64
len=34 cap=64
len=35 cap=64
len=36 cap=64
len=37 cap=64
len=38 cap=64
len=39 cap=64
len=40 cap=64
len=41 cap=64
len=42 cap=64
len=43 cap=64
len=44 cap=64
len=45 cap=64
len=46 cap=64
len=47 cap=64
len=48 cap=64
len=49 cap=64
len=50 cap=64
len=51 cap=64
len=52 cap=64
len=53 cap=64
len=54 cap=64
len=55 cap=64
len=56 cap=64
len=57 cap=64
len=58 cap=64
len=59 cap=64
[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59]

从上面的执行结果可以看出, Go语言会根据使用情况自动去扩展Slice的len以及cap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func printSliceInfo(s []string) {
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
}

func main() {
s := []string{"a", "b", "c"} // 首先创建了个Array, 然后对其view
printSliceInfo(s)

ss := make([]string, 16, 32) // 创建一个len为16, cap为32的string类型Slice
printSliceInfo(ss)
}

执行结果:

1
2
len=3 cap=3
len=16 cap=32

拷贝Slice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

func printSliceInfo(s []int) {
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
}

func main() {
s := []int{2015, 5, 9} // 首先创建了个Array, 然后对其view
printSliceInfo(s)
fmt.Println("s", s)

ss := make([]int, 4, 8) // 创建一个len为16, cap为32的string类型Slice
printSliceInfo(ss)
fmt.Println("ss", ss)

copy(ss, s) // 将s切片的数据拷贝到ss切片中
fmt.Println("ss", ss)
}

执行结果:

1
2
3
4
5
len=3 cap=3
s [2015 5 9]
len=4 cap=8
ss [0 0 0 0]
ss [2015 5 9 0]

删除Slice中的元素

对于Slice的删除功能, Go语言没有提供直接的操作语句, 但是可以通过Reslice来实现

需求: 删除Slice中的第3个元素 [12, 34, 56, 78, 90, 89, 72]

思路: 使用Reslice, s[:3] + s[4:] 取得的就是删除原切片第三个元素的数据, 但Go语言也没有提供Slice相加的功能, 只能通过append函数来实现, append(s[:3], s[4:]), 问题又来了, append的源码中要求第二参数必须是可变参数, 也就是一个值一个值的写在后面 func append(slice []Type, elems ...Type) []Type

Go语言中提供了相关的语法, 来解决s[4:]变成可变参数的问题

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
s := []int{12, 34, 56, 78, 90, 89, 72}
fmt.Println(s)

ss := append(s[:3], s[4:]...)
fmt.Println(ss)
}

执行结果:

1
2
[12 34 56 78 90 89 72]
[12 34 56 90 89 72]

删除头尾

删头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
s := []int{12, 34, 56, 78, 90, 89, 72}
fmt.Println(s)

fmt.Println("Poping from front")
front := s[0]
ss := s[1:]
fmt.Println("取出的头部元素为", front)
fmt.Println("取出头部元素后的Slice为", ss)
}

执行结果:

1
2
3
4
[12 34 56 78 90 89 72]
Poping from front
取出的头部元素为 12
取出头部元素后的Slice为 [34 56 78 90 89 72]

删尾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
s := []int{12, 34, 56, 78, 90, 89, 72}
fmt.Println(s)

fmt.Println("Poping from front")
tail := s[len(s)-1]
ss := s[:len(s)-1]
fmt.Println("取出的尾部元素为", tail)
fmt.Println("取出尾部元素后的Slice为", ss)
}

执行结果:

1
2
3
4
[12 34 56 78 90 89 72]
Poping from front
取出的尾部元素为 72
取出尾部元素后的Slice为 [12 34 56 78 90 89]