基本入门
- 为什么需要切片
先看一个需求:我们需要一个数组来保存学生的成绩,但是
学生的个数是不确定的
。解决方案:用到切片(可以理解为动态的数组)
- 切片的介绍
1、切片的英文slice
2、切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用类型的传递机制。
3、切片的使用和数组类似,遍历切片,访问切片的元素和求切片的长度len(slice)
都是一样。
4、切片的长度是可以变化的。因此是一个动态变化的数组。
5、切片定义的基本语法
var 切片名 []类型
比如:var a []int
- 快速入门
func main() {
//定义一个数组
var intArr [5]int = [...]int{1, 22, 33, 66, 99}
//定义一个切片
//slice是一个切片
//引用了intArr数组的,起始下标为1,终止下标为3(但是不包含3)
slice := intArr[1:3]
fmt.Println("intArr数组元素是 ", intArr)
fmt.Println("slice切片的元素是 ", slice)
fmt.Println("slice切片的长度是 ", len(slice))
fmt.Println("slice切片的容量是 ", cap(slice)) //容量 cap
//打印intArr[1]的内存中的地址 %p 格式化为0x开头的十六进制
fmt.Printf("intArr[1]的地址=%p\n", &intArr[1])
//打印slice的地址
fmt.Printf("slice的地址=%p\n", &slice)
slice[1] = 34
fmt.Println()
fmt.Println()
fmt.Println("intArr数组元素是 ", intArr)
fmt.Println("slice切片的元素是 ", slice)
}
切片中的内存布局(对上面代码的内存图解
)
- 总结
1、slice的确是一个引用类型
2、slice从底层来说,其实就是一个数据结构(struct结构体
)
type slice struct{
ptr *[2]int //指针
len int //长度
cap int //容量
}
切片使用(3种方式)
1、方式一
- 定义一个切片,用切片去引用前面创建好的数组。
var arr [5]int = [...]int{1, 2, 3, 4, 5}
slice := arr[1:3]
2、方式二
- 通过make来创建切片
语法:
var 切片名 [ ]type = make([ ]type , len , [cap])
参数说明:type
数据类型len
长度cap
容量
func main() {
var slice []int = make([]int, 4, 10)
//默认值
fmt.Println("默认值为:", slice)
// 长度
fmt.Printf("slice的长度为:%v\n", len(slice))
// 容量
fmt.Printf("slice的容量为:%v\n", cap(slice))
slice[0] = 100
slice[2] = 200
fmt.Println(slice)
}
- 切片案例及内存分析
var slice []int = make([]int, 5, 10)
//默认值
fmt.Println("默认值为:", slice)
// 长度
fmt.Printf("slice的长度为:%v\n", len(slice))
// 容量
fmt.Printf("slice的容量为:%v\n", cap(slice))
slice[1] = 10
slice[3] = 20
fmt.Println(slice)
小结
1、通过make方式可以指定切片的长度和容量
2、如果没有给切片的各个元素赋值,就会使用默认值[int,float => 0 ,string => "" ,bool => false
]
3、通过make方式创建的切片,默认数组只能由make底层维护,对外不可见,可能通过slice[下标]来访问。
3、方式三
定义一个切片,
直接指定一个数组
,使用原理类似make方式。
var slice []int = []int{1, 3, 5}
var strSlice []string = []string{"tom", "jack", "mary"}
切片的遍历
-
for
和for range
两种方式
func main() {
var arr [5]int = [...]int{10, 20, 30, 40, 50}
slice := arr[1:4] //20,30.40
for i := 0; i < len(slice); i++ {
fmt.Printf("i=%v v=%v ", i, slice[i])
}
fmt.Println()
for i, v := range slice {
fmt.Printf("i=%v v=%v \n", i, v)
}
}
切片的注意事项
1、切片初始化时 var slice = arr[startIndex,endIndex]
说明:从
arr
数组下标为startIndex
,取到下标为endIndex
的元素(不含arr[endIndex]
)
2、切片初始化时,仍然不能越界
- 2.1
var slice = arr[0:end]
可以简写var slice = arr[:end]
- 2.2
var slice = arr[start:len(arr)]
可以简写var slice = arr[start:]
- 2.3
var slice = arr[0:len(arr)]
可以简写var slice = arr[:]
3、cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素。
4、切片定义完后,还不能使用,因为本身是一个空,需要让其引用一个数组或或者make
一个空间供切片来使用。
5、切片可以继续切片(案例演示)
var arr [5]int = [...]int{10, 20, 30, 40, 50}
// slice := arr[:] //全部
slice := arr[1:4] //20,30.40
slice2 := slice[1:2] //30
slice2[0] = 100
fmt.Println("arr=", arr)
fmt.Println("slice=", slice)
fmt.Println("slice2=", slice2)
6、用append内置函数来给切片动态增加
var slice []int = []int{100, 200, 300}
fmt.Println("slice=", slice)
//通过append直接给slice追加具体的元素。元素类型要一致
slice = append(slice, 400, 500, 600)
fmt.Println("slice=", slice)
//切片追加切片
slice = append(slice, slice...)
fmt.Println("slice=", slice)
append
操作的底层原理分析:
6.1、切片append
操作的本质就是对数组扩容
6.2、go
底层会创建一下新的数组newArr
(按扩容后大小)
6.3、将slice
原来包含的元素拷贝到新的数组newArr
6.4、slice
重新引用到newArr
6.5、注意newArr
是在底层来维护的。程序员不可见。
-
append内存图分析
7、切片的copy(拷贝)
var slice1 []int = []int{1, 2, 3, 4, 5}
var slice2 = make([]int, 10)
copy(slice2, slice1)
fmt.Println("slice1=", slice1)
fmt.Println("slice2=", slice2)
slice1[2] = 100
fmt.Println("修改后的slice1=", slice1)
fmt.Println("最后slice2=", slice2)
copy
的时候要求数据类型都是切片
;
按上面代码,slice1
和slice2
的数据空间是独立的
,相互不影响。
string 和slice
1、string底层是一个byte数组,因此string也可以进行切片处理。
str := "hello@atguigu"
//使用切片来获取数组
slice := str[6:]
fmt.Println("slice=", slice)
2、string和切片在内存的形式,以'abcd'画出内存示意图
3、string是不可变的,也就说不能通过str[0] = 'z'方式来修改字符串
4、如果需要修改字符串,可以先将string -> []byte /或者 []rune -> 修改 -> 重写转成string
str := "hello@atguigu"
//将字符串转为byte数组
arr1 := []byte(str)
arr1[0] = 'z'
//将切片转为字符串
str = string(arr1)
fmt.Println("str = ", str)
//byte类型只能处理英文和数字,不能处理中文
//原因是英文和数字占一个字节,而中文会占用3个字节
//可以通过[]rune 来处理
arr2 := []rune(str)
arr2[0] = '北'
str = string(arr2)
fmt.Println("str = ", str)
byte
类型只能处理英文和数字,不能处理中文
原因是英文和数字占一个字节,而中文会占用3个字节
可以通过[]rune
来处理
切片练习
- 编写一个函数
fbn(n int)
,要求完成
1、可以接收一个n int
2、能将斐波那契的数列入到切片中
3、提示,斐波那契的数列形式
arr[0] = 1; arr[1] = 1; arr[2] = 2; arr[3] = 3; arr[4] = 5 ; arr[5] = 8
package main
import (
"fmt"
)
func fbn(n int) []uint64 {
//声明一个切片 大小是 n
fbnSlice := make([]uint64, n)
fbnSlice[0] = 1
fbnSlice[1] = 1
//for循环来存放 斐波那契数
for i := 2; i < n; i++ {
fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2]
}
return fbnSlice
}
func main() {
//思数:
//1、定义一个大小n的切片
//2、下标为0和1的切片元素值都为1
//3、剩下for循环 return 回切片
fbnSlice := fbn(30)
fmt.Println("fbnSlice=", fbnSlice)
}
网友评论