go的数组,因定义后,长度固定,不可增删其中元素,不方便,
go的切片slice,是一个具有相同类型元素的可变长序列的组合,是基于数组类型做的一层封装,灵活,支持自动扩容
go切片是引用类型,内部结构:包括:地址、长度、容量,切片一般用于快速的操作一块数据集合
语法:
1
2
3
4
5
|
var name []T
name为变量名
T为元素类型
和数组不同之处:[]内没有指定长度,也没有...利用编译器推测长度
|
示例
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
|
package main
import "fmt"
func main() {
var a []string // 声明一个字符串切片,默认是空
var b = []int{} // 声明一个整型切片并初始化,没赋值也是空
var c = []bool{false,true}
var d = []bool{false,true}
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(a == nil) // 只有a等于nil
fmt.Println(b == nil)
fmt.Println(c == nil)
fmt.Println(d == nil)
// 切片是引用类型,不支持直接比较,只能和nil比较
}
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[]
[]
[false true]
true
false
false
false
|
切片有自己的长度、len()函数可以求得;cap()函数可以求切片的容量;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main
import "fmt"
func main() {
// 1,简单的切片表达式,s := a[low:high]
a := [5]int{1, 2, 3, 4, 5}
s := a[1:3] // s := a[low:high]
fmt.Printf("s:%v len(s):%v cap(s):%v \n", s, len(s), cap(s))
}
s:[2 3] len(s):2 cap(s):4
|
切片表达式从字符串、数组、指向数组或切片的指针构造子字符串或切片、有2种变体;
- 指定low和high2的索引界限值
- 除了指定low和high还指定容量的完整形式,加上max
1、简单切片表达式:
切片底层就是数组、可以基于数组通过切片表达式,得到切片;切片表达式中的low和high表示一个索引范围,左包含、右不包含;例如:下面代码从数组a,选出1<=索引值<3的元素组成切片s,得到切片长度=high-low,容量等于得到的切片的底层数组的容量(但要从切的位置开始数,如下,从a的第一个下标开始数,容量是4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main
import "fmt"
func main() {
// 1,简单的切片表达式,s := a[low:high]
a := [5]int{1, 2, 3, 4, 5}
s := a[1:3] // s := a[low:high]
fmt.Printf("s:%v len(s):%v cap(s):%v \n", s, len(s), cap(s))
}
s:[2 3] len(s):2 cap(s):4
|
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
|
package main
import "fmt"
func main() {
a := [5]int{1, 2, 3, 4, 5}
// 2, 切片表达式的省略
s1 := a[2:]
s2 := a[:3]
s3 := a[:]
fmt.Printf("%v", s1)
fmt.Printf("%v", s2)
fmt.Printf("%v", s3)
fmt.Println("")
// 3,对切片再切片时,high的上限边界是切片的容量,而不是长度!
// 在切片s 的基础上,再切出一个s4,s是[2 3]
// s4的high上限是s的容量,即4,而low是从0开始计算,即s的0是2,从这里开始计算,那么s的3,就对应a的5,所以s4就是一个5,长度为1,容量为1
// a 1 2 3 4 5
// s 2 3
// s4
s4 := s[3:4]
fmt.Printf("s4:%v len(s4):%v cap(s4):%v \n", s4, len(s4), cap(s4))
// 4,完整的切片表示式: a[low:high:max]
// a := [5]int{1, 2, 3, 4, 5}
// max-low就是该切片的cap容量,max的作用是限制切片的容量,没有max,默认会到数组的最后一个元素
t := a[1:3:5]
fmt.Printf("t值%v t长度%v t容量%v\n", t, len(t), cap(t))
}
[3 4 5][1 2 3][1 2 3 4 5]
s4:[5] len(s4):1 cap(s4):1
t值[2 3] t长度2 t容量4
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import "fmt"
func main() {
// 5,使用make()函数构造切片,而不是基于数组去切
// 语法: make([]T ,size, cap) T 是元素类型,size是元素数量,cap是切片的容量,即元素数量的上限
slice1 := make([]int, 2, 10)
fmt.Println(slice1)
fmt.Println(len(slice1)) // 长度为2
fmt.Println(cap(slice1)) // 容量为10
}
[0 0]
2
10
|
切片的本质:对数组进行的封装
切片:包含3个信息:底层数组的指针、切片的长度len、切片的容量cap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import "fmt"
func main() {
// 6,判断切片是否为空,用len(s) == 0判断,而不是s == nil
slice2 := make([]int,0,10)
if len(slice2) == 0 {
fmt.Println("slice2 is null")
}
}
slice2 is null
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import "fmt"
func main() {
// 7, 切片不能直接比较,切片唯一能比较的是nil,容量和长度都是0的切片,不一定义等于nil,反过来,则成立
// var slice3 []int // len=0 cap =0 slice3=nil
// slice4 := []int{} // len=0 cap =0 ,slice4!=nil
// slice5 := make([]int,0) // // len=0 cap =0 ,slice5!=nil
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import "fmt"
func main() {
// 8,切片的赋值拷贝,切片是引用类型,基于同一个数组的切片,一个改了,会影响其他切片
slice6 := make([]int,3)
slice7 := slice6 // 此时6个7公用一个底层数组
slice7[0] = 233
fmt.Println(slice6)
fmt.Println(slice7)
}
[233 0 0]
[233 0 0]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import "fmt"
func main() {
// 9,切片的遍历,和数组的遍历一致,支持for和for range遍历
// for i := 0, i < len(slice6); i++ {
// fmt.Println(i, slice6[i] )
// }
for index,value := range slice6 {
fmt.Println(index,value)
}
}
0 233
1 0
2 0
|
根据打印结果,看出,从一个空值的切片开始算,默认扩容策略是1,2,4,8,16,前一次的2倍,其指针指向的内存地址,也会随之扩容,而变化
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
|
package main
import "fmt"
func main() {
// 切片的追加
var s []int
s = append(s, 1) // 通过var定义的空值切片,可以在append值直接追加,无需初始化
fmt.Println(s)
s = append(s, 2, 3, 4)
fmt.Println(s)
// 切片的自动扩容
// 从一个空值的切开开始算,默认扩容策略是1,2,4,8,16,前一次的2倍
// 每个切片都会指向一个底层数组,当底层数组不能够容纳新元素时,切片会根据”策略“进行扩容,此时切片指向的数组指针,就会发生改变
var slice []int
for i := 0; i < 10; i++ {
slice = append(slice, i)
fmt.Printf("%v %d %d %p\n", slice, len(slice), cap(slice), slice)
}
// 切片,一次追加多个元素
slice = append(slice,666,233,999)
fmt.Println(slice)
}
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1]
[1 2 3 4]
[0] 1 1 0xc000012120
[0 1] 2 2 0xc000012140
[0 1 2] 3 4 0xc00000a460
[0 1 2 3] 4 4 0xc00000a460
[0 1 2 3 4] 5 8 0xc0000102c0
[0 1 2 3 4 5] 6 8 0xc0000102c0
[0 1 2 3 4 5 6] 7 8 0xc0000102c0
[0 1 2 3 4 5 6 7] 8 8 0xc0000102c0
[0 1 2 3 4 5 6 7 8] 9 16 0xc000018100
[0 1 2 3 4 5 6 7 8 9] 10 16 0xc000018100
[0 1 2 3 4 5 6 7 8 9 666 233 999]
|
对应源码:$GOROOT/src/runtime/slice.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap
}
}
}
|
1、切片是引用型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import "fmt"
func main() {
// copy复制切片demo
a := []int{1,3,5,7,9}
b := a
fmt.Println(a)
fmt.Println(b)
b[0] = 100
fmt.Println(a)
fmt.Println(b)
}
// 切片是引用型,a和b切片,指向的都是同一个内存地址,改一个,另一个也会变,
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1 3 5 7 9]
[1 3 5 7 9]
[100 3 5 7 9]
[100 3 5 7 9]
|
2、对切片进行复制,使得对副本的修改,不影响原切片
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
|
package main
import "fmt"
func main() {
// copy复制切片demo
a := []int{1,3,5,7,9}
b := make([]int,5,5)
copy(b,a)
fmt.Println(a)
fmt.Println(b)
b[0] = 100
fmt.Println(a)
fmt.Println(b)
}
// 可以看到,修改b,对a无影响,因为b是a的另一份数据拷贝,指向不同内存地址
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1 3 5 7 9]
[1 3 5 7 9]
[1 3 5 7 9]
[100 3 5 7 9]
|
go本身不支持切片,但利用append函数,可以实现切片中元素的删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import "fmt"
func main() {
// 切片的元素删除demo
a := []int{1, 4, 5, 7, 8, 9}
fmt.Println(a)
// 现删除5,其索引为2
a = append(a[:2], a[3:]...)
fmt.Println(a)
// 通过的格式:要删除a切片中,某个index的元素:a = append(a[:index],a[index+1:]...)
}
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1 4 5 7 8 9]
[1 4 7 8 9]
|
-
请写出下面代码的输出结果。
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
|
func main() {
var a = make([]string, 5, 10)
for i := 0; i < 10; i++ {
a = append(a, fmt.Sprintf("%v", i))
}
fmt.Println(a)
}
// 测试如下:
// 原本长度5,容量10,前五个是默认的空字符串"",后五个无
// 经过for循环,追加了10个字符串,长度变为15,容量因扩容策略,而翻倍,变成了20
package main
import "fmt"
func main() {
// 练习1
var a = make([]string, 5, 10)
for i := 0; i < 10; i++ {
a = append(a, fmt.Sprintf("%v", i))
}
fmt.Println(a)
fmt.Printf("len of a:%v", len(a))
fmt.Printf("cap of a:%v", cap(a))
}
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[ 0 1 2 3 4 5 6 7 8 9]
len of a:15cap of a:20
|
-
2.请使用内置的sort
包对数组var a = [...]int{3, 7, 8, 9, 1}
进行排序(附加题,自行查资料解答)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package main
import (
"fmt"
"sort"
)
func main() {
// 练习2
var a = [...]int{3, 7, 8, 9, 1}
//升序
sort.Ints(a[:])
fmt.Println(a)
// 降序
sort.Sort(sort.Reverse(sort.IntSlice(a[:])))
fmt.Println(a)
}
D:\workstation\mycode\gocode\src\learngo\basic_grammar\06slice>go run main.go
[1 3 7 8 9]
[9 8 7 3 1]
|