一.切片的使用
方式一
定义一个切片,然后让切片去引用一个已经创建好的数组,案例如下:
package main
import (
"fmt"
)
func main() {
//演示切片的基本使用
var intArr [5]int = [...]int{1,2,3,4,5}
//声明/定义
//slice := intArr[1:3]
//1.slice 就是切片名
//2.intArr[1:3] 表示slice引用到intArr这个数组
//3.引用intArr这个数组的起始下标为1,最后的下标为3(但不包含3)
slice := intArr[1:3]
fmt.Println("intArr = ", intArr) //[1 2 3 4 5]
fmt.Println("slice 的元素是 = ", intArr) //[2 3]
fmt.Println("slice 的元素个数是= ", len(slice)) // 2
fmt.Println("slice 的容量 = ", cap(slice)) // 切片的容量是可以动态变化的
}
方式二
通过make来创建切片
语法如下:
var 切片名 []type = make([]type, len, [cap])
参数说明:
type 就是数据类型, len 切片大小, cap 指定切片容量,cap是可选的,如果分配了cap,则要求 cap >= len
案例如下:
package main
import (
"fmt"
)
func main(){
//演示切片的使用 make
var slice []float64 = make([]float64, 5, 10)
fmt.Println(slice) // [0 0 0 0 0]
slice[1] = 11
slice[3] = 33
//对于切片,必须make使用
fmt.Println(slice) // [0 11 0 33 0]
fmt.Printf("slice 的size = %d", len(slice)) // 5
fmt.Printf("slice 的容量 = %d", cap(slice)) //10
}
对上述代码画图分析:
小节:
(1).通过make方式创建的切片可以指定切片的大小和容量
(2).如果没有给切片的各个元素赋值,那么就会使用默认值(int,float => 0, string => "", bool => false)
(3).通过make方式创建的切片对应的数组是由make底层维护的,对外不可见,即只能通过slice去访问各个元素
方式三
定义一个切片,直接就指定具体数组,使用原理类似make的方式
//方式3
//定义一个切片,直接指定具体数组,使用原理类似make的方式
var slice1 []int = []int{11,22,33}
fmt.Println(slice1) // [11 22 33]
fmt.Printf("slice1 的size = %d", len(slice1)) // 3
fmt.Printf("slice1 的容量 = %d", cap(slice1)) // ? 可变: 这里应该是3
方式一和方式二的区别
方式一是直接引用数组,这个数组是事先存在的,程序员可见的.
方式二是通过make来创建切片,make也会创建一个切片,是由切片底层维护的,程序员不可见
二.切片的遍历
方式一
for循环常规方式遍历
方式二
for-range结构遍历
//使用for循环遍历
var arr [5]int = [...]int{1,2,3,4,5}
// slice := arr[1:4] // [2 3 4]
slice := arr[:] //取全部数据: [1 2 3 4 5]
// slice := arr[:4] //取[0,4): [1 2 3 4]
// slice := arr[2:] //取[2,4]: [3 4 5]
for i := 0 ;i< len(slice);i++{
fmt.Printf("slice[%d] = %v ", i, slice[i]) // slice[0] = 2 slice[1] = 3 slice[2] = 4
}
//使用for-range遍历
for index, value := range slice {
fmt.Printf("slice[%d] = %v \n", index, value) // slice[0] = 2 slice[1] = 3 slice[2] = 4
}
三.注意事项和使用细节
切片初始化时, var slice = arr[startIndex:endIndex]
说明:
从arr数组下标为startIndex,取到下标为endIndex的元素(不含arr[endIndex)
切片初始化时,仍热不能越界,范围在[0~len(arr)]之间,但是可以动态增长
1.var slice = arr[0:end] 可以简写成 var slice = arr[:end]
2.var slice = arr[start:len(arr)] 可以简写成 var slice = arr[start:]
var slice = arr[0:len(arr)] 可以简写成 var slice = arr[:]
3.cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素
4.切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间切片来使用
5.切片可以继续切片
//使用for循环遍历
var arr [5]int = [...]int{1,2,3,4,5}
// slice := arr[1:4] // [2 3 4]
slice := arr[:] //取全部数据: [1 2 3 4 5]
// slice := arr[:4] //取[0,4): [1 2 3 4]
// slice := arr[2:] //取[2,4]: [3 4 5]
for i := 0 ;i< len(slice);i++{
fmt.Printf("slice[%d] = %v ", i, slice[i]) // slice[0] = 2 slice[1] = 3 slice[2] = 4
}
//使用for-range遍历
for index, value := range slice {
fmt.Printf("slice[%d] = %v \n", index, value) // slice[0] = 2 slice[1] = 3 slice[2] = 4
}
// slice2 := slice[2:3] // [3]
slice2 := slice[2:4] // [3 4]
fmt.Printf("slice2=%v\n", slice2) // [3 4]
slice2[0] = 100 //因为slice,slice2指向的数据空间是同一个,因此slice2[0]=100变化了,其他的也会发生变化
fmt.Printf("slice2=%v\n", slice2) // [100 4]
fmt.Printf("slice=%v\n", slice) // [1 2 100 4 5]
fmt.Printf("arr=%v\n", arr) // [1 2 100 4 5]
6.用append内置函数,可以对切片进行动态追加
//用append内置函数,可以对切片进行动态追加
var slice3 []int = []int{11,22,33,44}
//注意: append动态追加后,返回一个新的切片,原来的切片不会发生变化, 除非把append后的切片赋给原来的切片
// slice4 := append(slice3, 55, 66, 77)
slice3 = append(slice3, 55, 66, 77)
fmt.Printf("slice3=%v\n", slice3) //11,22,33,44, 55, 66, 77
// fmt.Printf("slice4=%v\n", slice4) //11,22,33,44, 55, 66, 77
//通过append将切片slice3(可以是其他切片)加给slice3
// slice3 = append(slice3, slice3...)
slice3 = append(slice3, slice...)
fmt.Printf("slice3=%v\n", slice3)
切片append操作底层原理分析
(1).切片append操作本质是对数组扩容
(2).go底层会创建一个新的位数组,newArr (安装扩容后大小)
(3).将slice原来包含的元素拷贝到新的数组newArr
(4).slice重新引用到newArr
(5).注意:newArr是在底层维护的,程序员不可见
7.切片的拷贝操作
//切片的拷贝操作
//使用copy内置函数完成切片的拷贝
var slice4 []int = []int{1,2,3,4}
var slice5 = make([]int, 10)
copy(slice5, slice4) //拷贝slice4到slice5
fmt.Printf("slice4= %v\n", slice4) // 1,2,3,4
fmt.Printf("slice5= %v\n", slice5) //1,2,3,4,0,0,0,0,0,0
对上面代码的说明:
copy(para1,para2)参数的类型就是切片
按照上面代码来看,slice4,slice5的数据空间是独立,互不影响的,slice4切片中的数据改变不会影响到slice5中的数据
8.思考: 下面代码有没有错误
var a[]int = []int{1, 2, 3, 4} //定义一个切片
var slice = make([]int, 1) //定义一个切片,默认为[0]
fmt.Println(slice) // [0]
copy(slice, a)
fmt.Println(slice) // [1]
说明:
上面代码没有问题,可以运行,最后输出[1]
原因:
copy完成切片的拷贝,不过slice的len为1,故只会拷贝a切片中的第一个元素到slice
9.数组和切片的相互转换
//数组转换为切片:
//1.使用切片操作符[:],它可以返回从指定索引到数组末尾的所有元素,例如:
// 定义一个数组
arr := [5]int{1, 2, 3, 4, 5}
// 将数组转换为切片
slice := arr[:]
//2.使用copy函数
array := [5]int{1, 2, 3, 4, 5}
slice := make([]int, len(array))
copy(slice, array[:])
fmt.Println(slice) // 输出:[1 2 3 4 5]
//切片转换为数组:Go语言中,由于切片是动态数组,不能直接转换为数组,但可以通过copy函数将切片的内容复制到一个新的数组中
slice := []int{1, 2, 3, 4, 5}
var array [5]int
copy(array[:], slice)
fmt.Println(array) // 输出:[1 2 3 4 5]
fmt.Print(reflect.TypeOf(array).Kind()) // array: 通过类型断言判断是一个数组