go语言开发基础20 - 之go语言里的切片类型
一、说明
- 切片是go里面的一种数据类型,切片的长度是可变的(切片底层是引用的数组类型),因此切片也可以称为可变数组。
- 切片的底层是引用的数组类型,因此切片是引用类型
-
切片遍历方式和数组一样,可以用len()函数求切片的长度
-
cap可以求出slice的最大容量,0<=len(slice)<=cap(array),其中array是slice引用的数组
-
切片与数据的内部调用:将数组赋值给切片后,切片里就有一个指针指向数组。
二、切片的定义、初始化与便利
2.1、切片的定义
var str []string // 定义string类型的切片 var arr []int // 定义int类型的切片切片
切片定义后不可以直接使用,需要初始化切片后才可以进行操作
2.2、切片初始化
方法一:使用数组给切片初始化
var a [5]int // 定义int类型长度为5的数组,int类型数组没赋值之前默认用0占位 var sli []int = a[1:3] // 将数组a下标是1和2的值赋值给切片了,切片给数组赋值时var sli []int = array[start:end]包含start到end之间下标的的元素,但不包含end
fmt.Println(sli) // 查看切片:结果为:[0 0]
sli[0] = 100 // 修改撇片下标为0的值,切片是引用类型,这里的修改可以将数组的值也修改了
fmt.Println(sli) // 结果为:[100 0]
fmt.Println(a1) // 此时查看数组,发现数组的值也被修改了,结果为:[0 100 0 0 0]
方法二:使用make方法给切片初始化
var slice []int // 定义切片 slice = make([]int, 10) // 初始化切片,切片长度为10 slice[0] = 3 // 给切片下标0的赋值 fmt.Println(slice) // 结果为:[3 0 0 0 0 0 0 0 0 0]
2.3、切片循环
func loopSlice() {
var sli []int // 定义切片
sli = make([]int, 10) // 使用make方法初始化切片,切片长度为10
sli[0] = 20 // 给切片下标0赋值为20
// 循环,使用rand随机数方法给sli切片下标1-9赋值
for i := 1; i < len(sli); i ++ {
num := rand.Intn(10)
sli[i] = i+num
}
// 两种循环方法
fmt.Println("循环方法一")
for i := 0; i < len(sli); i ++ {
fmt.Printf("index: %d, value: %d\n", i, sli[i])
}
fmt.Println("循环方法二")
for index,value := range sli {
fmt.Printf("index: %d, value: %d\n", index, value)
}
}
// 执行结果为:
循环方法一
index: 0, value: 20
index: 1, value: 2
index: 2, value: 9
index: 3, value: 10
index: 4, value: 13
index: 5, value: 6
index: 6, value: 14
index: 7, value: 12
index: 8, value: 8
index: 9, value: 15
循环方法二
index: 0, value: 20
index: 1, value: 2
index: 2, value: 9
index: 3, value: 10
index: 4, value: 13
index: 5, value: 6
index: 6, value: 14
index: 7, value: 12
index: 8, value: 8
index: 9, value: 15
三、切片长度与容量获取
切片的长度和容量有时候是不一样的。用cap()函数来获取切片的容量。
func testSlice() {
var slice []int // 定义int类型的切片(空切片不可以直接赋值)
var arr [5]int = [...]int{1, 2, 3, 4, 5} // 定义数组
slice = arr[2:3] // 将数组下标2到3的值(不包含3)赋值给切片(也支持make的方式给切片初始化)
fmt.Println(slice) // 打印切片的结果,结果为:[3]
// 查看切片长度和容量
// 长度和容量一样示例
fmt.Println("1 slice len:", len(slice)) // 长度,结果为:1 slice len: 1
fmt.Println("1 slice cap:", cap(slice)) // 容量,结果为:1 slice cap: 3
// 长度和容量不一样示例
slice = slice[0:1] // 切片支持自己给自己赋值
fmt.Println("2 slice len:", len(slice)) // 长度,结果为:2 slice len: 1
fmt.Println("2 slice cap:", cap(slice)) // 容量,结果为:2 slice cap: 3
}
四、切片的排序与查找
这里的排序和查找功能是使用sort包下的方法来实现的,sort包直接导入即可。
4.1、整数类型的切片排序与查找
package main
import (
"sort"
"fmt"
)
func testIntSort() { var a = [...]int{1, 34, 45, 7, 8, 3} // 定义数组(数组是值类型,要改变数组里面的东西要通过切片来修改)
// 使用sort包的Ints()方法对切片进行排序 sort.Ints(a[:]) // 只能对切片排序,所以要传切片(因为数组是值类型,传的是副本,不能被排序) fmt.Println(a) // 结果为:[1 3 7 8 34 45] index := sort.SearchInts(a[:], 8) // 在a(切片)里查找8的下标,切片必须是有序的否则查找的结果不准(需要先排序) fmt.Println("Int Search index :", index) // 结果为:Int Search index : 3 }
4.2、字符串类型的切片排序与查找
package main
import (
"sort"
"fmt"
)
func testStrSort() {
var a = [...]string{"abc", "dsvc", "rsg", "fdv", "AB"} // 定义string类型的可变数组
sort.Strings(a[:]) // 只能对切片排序,所以要传切片(因为数组是值类型,传的是副本,不能被排序)
fmt.Println(a) // 结果为:[AB abc dsvc fdv rsg]
index := sort.SearchStrings(a[:], "rsg") // 在切片里查找字符串"rsg"的下标,切片必须是有序的否则查找的结果不准(需要先排序)
fmt.Println("String Search index :", index) // 结果为:String Search index : 4
}
4.3、浮点类型的切片排序查找
package main
import (
"sort"
"fmt"
)
func testFloatSort() {
var a = [...]float64{1.1, 3.5, 5.8, 9.9} // 定义float64类型的数组
sort.Float64s(a[:]) // 只能对切片排序,所以要传切片(因为数组是值类型,传的是副本,不能被排序)
fmt.Println(a) // 结果为:[1.1 3.5 5.8 9.9]
index := sort.SearchFloat64s(a[:], 5.8)
fmt.Println("Float Search index :", index) // 结果为:Float Search index : 2
}
五、切片之间不能直接比较
切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和nil比较。 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil,例如下面的示例:
var s1 []int //len(s1)=0;cap(s1)=0;s1==nil
s2 := []int{} //len(s2)=0;cap(s2)=0;s2!=nil
s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
所以要判断一个切片是否是空的,要是用len(s) == 0来判断,不应该使用s == nil来判断。
六、使用copy()给切片赋值
6.1、copy函数的语法
copy(destSlice, srcSlice) //srcSlice: 数据来源切片、destSlice: 目标切片
6.2、示例
func main() {
a := []int{1, 2, 3, 4, 5} // 定义a切片并赋值
c := make([]int, 5, 5) // 定义int类型b切片并初始化b切片长度和容量都是5(说明两个切片的底层数据不是用的同一个数组)
copy(c, a) //使用copy()函数将切片a中的元素复制到切片c
fmt.Println(a) //结果为:[1 2 3 4 5]
fmt.Println(c) //结果为:[1 2 3 4 5]
c[0] = 1000 // 修改c切片下标为0的值
fmt.Println(a) //打印结果:[1 2 3 4 5]
fmt.Println(c) //打印结果:[1000 2 3 4 5]
}
七、append()往切片里追加元素
Go语言的内建函数append()可以为切片动态添加元素。 每个切片会指向一个底层数组,这个数组能容纳一定数量的元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时。 举个例子:
func main() {
//append()添加元素和切片扩容
var numSlice []int
for i := 0; i < 10; i++ {
numSlice = append(numSlice, i)
fmt.Printf("%v len:%d cap:%d ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
}
}
// 执行结果为:
[0] len:1 cap:1 ptr:0xc0000a8000
[0 1] len:2 cap:2 ptr:0xc0000a8040
[0 1 2] len:3 cap:4 ptr:0xc0000b2020
[0 1 2 3] len:4 cap:4 ptr:0xc0000b2020
[0 1 2 3 4] len:5 cap:8 ptr:0xc0000b6000
[0 1 2 3 4 5] len:6 cap:8 ptr:0xc0000b6000
[0 1 2 3 4 5 6] len:7 cap:8 ptr:0xc0000b6000
[0 1 2 3 4 5 6 7] len:8 cap:8 ptr:0xc0000b6000
[0 1 2 3 4 5 6 7 8] len:9 cap:16 ptr:0xc0000b8000
[0 1 2 3 4 5 6 7 8 9] len:10 cap:16 ptr:0xc0000b8000
从上面的结果可以看出:
1.append()函数将元素追加到切片的最后并返回该切片。
2.切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍。
3.append()函数还支持一次性追加多个元素。 例如:
var citySlice []string
// 追加一个元素
citySlice = append(citySlice, "北京")
// 追加多个元素
citySlice = append(citySlice, "上海", "广州", "深圳")
// 追加切片
a := []string{"成都", "重庆"}
citySlice = append(citySlice, a...)
fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]
八、其他类型的切片与接口类型的切片
空接口因为没有实现任何方法,所以支持任意类型的赋值操作,但是其他类型的切片和接口类型的切片不可以直接赋值(直接赋值会报错),需要通过循环一个一个的来实现赋值操作,具体示例如下:
package main
import "fmt"
func main() {
var a []int = []int{22, 33, 44, 55}
var b []interface{}
// b = a // 这里直接赋值会报错,需要循环进行赋值
for _, v := range a {
b = append(b, v)
}
fmt.Println(b) // 结果为:[22 33 44 55]
}

浙公网安备 33010602011771号