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 \ 06 slice > 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 \ 06 slice > 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 \ 06 slice > 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 \ 06 slice > 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 \ 06 slice > 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 \ 06 slice > go run main . go
[ 0 1 2 3 4 5 6 7 8 9 ]
len of a : 15 cap 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 \ 06 slice > go run main . go
[ 1 3 7 8 9 ]
[ 9 8 7 3 1 ]
复制