赞
踩
- 切片(slice)通过指针引用底层数组,当需要时,会申请更大的内存空间将当前数据复制过去, 以实现类似动态数组的功能。
- 切片的长度是不固定的,可以追加数据,切片的底层是一个结构体
- 切片没有自己的任何数据,它只是底层数组的一个引用,对切片所做的任何修改都将反映在底层数组中。
type slice struct {
array unsafe.Pointer // 指向底层数组某元素的指针,也是切片的起始元素
len int // 切片的长度
cap int // 切片的容量
}
怎么区分数组和切片:
数组是必须要有长度,不管是显式还是推导都要有长度,但是切片不是一定要显式长度
切片的创建:
可直接创建切片对象,无需预先准备数组。因为是引用类型,必须使用make函数或显式初始化语句,它会自动完成底层数组内存分配
注意:切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3sCHkZTU-1677512960761)(/Users/itzhuzhu/Library/Application Support/typora-user-images/image-20220901152220185.png)]
示例:
s := make([]byte,5)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxzb2rBx-1677512960762)(/Users/itzhuzhu/Library/Application Support/typora-user-images/image-20230213101616592.png)]
定义切片:
// 仅定义没有写make就相当于创建了切片里面是空的
var slice [] type
格式2:
var slice = []type{}
make函数创建切片:
// len:切片的长度 cap:切片的容量 cap不能大于len,且可省略,默认和len相同
// len和cap:长度是已经初始化的空间,容量是已经开辟的空间,包括已经初始化的空间和空闲的空间
// cap是一个内置函数,用于统计切片的容量,也就是最大可以存放多少个元素
var slice = make([]type, len, cap)
使用make创建的切片是有空间的,非make定义的切片是空的(无法获取地址)
func main() {
var slice []int // 普通格式创建的slice,空切片
slice2 := []int{} // 格式2创建的sslice,空切片
slice3 := make([]int, 2) // make函数创建的slice,省略cap,cap=len
slice4 := make([]int, 2, 3) // make函数创建的slice,自定义cap
fmt.Println("slice:", slice)
//fmt.Println("slice的内存地址:", &slice[0]) // panic: runtime error: index out of range [0] with length 0
fmt.Println("slice2:", slice2)
//fmt.Println("slice2的内存地址:", &slice2[0]) // panic: runtime error: index out of range [0] with length 0
fmt.Println("slice3:", slice3, "len:", len(slice3), "cap:", cap(slice3), "slice3的内存地址:", &slice3[0])
fmt.Println("slice4:", slice4, "len:", len(slice4), "cap:", cap(slice4), "slice4的内存地址:", &slice4[0])
}
输出:
slice: []
slice2: []
slice3: [0 0] len: 2 cap: 2 slice3的内存地址: 0x14000122010
slice4: [0 0] len: 2 cap: 3 slice4的内存地址: 0x1400012a018
初始化格式:
/// 普通格式创建的切片
var slice []type
var slice = [] type{数据1,数据2,数据3}
// make函数方式创建的切片可以通过append和循环初始化
slice = append(slice,数据1,数据2...)
演示:
func main() {
var slice []int
slice = append(slice, 1, 2, 3, 4, 5)
slice[0] = 100
var slice2 = []int{}
slice2 = append(slice2, 1, 2, 3, 4, 5)
slice2[0] = 100
slice3 := make([]int, 2, 3)
slice3 = append(slice3, 1, 2, 3, 4, 5)
slice3[0] = 100
fmt.Println("slice:", slice)
fmt.Println("slice2:", slice2)
fmt.Println("slice3:", slice3)
}
输出:
slice: [100 2 3 4 5]
slice2: [100 2 3 4 5]
slice3: [100 0 1 2 3 4 5]
引用数组创建切片:
func main() {
// 1. 创建数组
arr := [...]int{1, 2, 3, 4, 5}
// 2.创建切片并引用数组
slice := arr // 相当于引用数组全部数据
// arr[1:3]:引用了数组arr的数据,从索引1到索引3-1(包含索引1但是不包含索引3,也就是arr[2,3])
slice2 := arr[1:3]
fmt.Println("slice:", slice)
// 现在切片中只有 [2 3],也就是索引0和1,如果用取slice[2]就会越界
fmt.Println("slice2:", slice2)
//fmt.Println("slice:", slice[2]) // 报错
fmt.Println("slice2中元素的个数:", len(slice2))
fmt.Println("slice2的容量:", cap(slice2))
}
输出:
slice: [1 2 3 4 5]
slice2: [2 3]
slice2中元素的个数: 2
slice2的容量: 4
当多个切片共享相同的底层数组时,修改任意切片既为修改底层数组
func main() {
slice := make([]int, 2, 3)
slice[0] = 1
slice[1] = 2
slice2 := slice
slice2[0] = 100
fmt.Println(slice)
fmt.Println(slice2)
}
输出:
[100 2]
[100 2]
注意下面两种定义方式的区别。前者仅定义了一个[ ]int类型变量,并未执行初始化操作,而后者则用初始化表达式完成了全部创建过程
func main() {
var a []int
b := []int{}
fmt.Println(a == nil, b == nil) // true false
}
二维切片
func main() {
// 有任意个一维切片,每个一维切片中有任意个数据
x := [][]int{
{1, 2},
{10, 20, 30},
{100},
}
fmt.Println(x[0])
fmt.Println(x[1])
fmt.Println(x[2])
}
输出:
[1 2]
[10 20 30]
[100]
遍历和数组一样可以使用普通的for循环和range遍历得到
func main() {
slice := []int{1, 2, 3, 4, 5}
for i := 0; i < len(slice); i++ {
fmt.Print(slice[i])
}
for _, v := range slice {
fmt.Println(v)
}
}
append函数是向切片的末尾添加数据,如果添加的内容超出了切片初始定义的容量,切片会自动扩容,每次扩容都是一个新的内存,和原切片无关联,所以如果是通过参数传递的方式,使用append添加数据,但是不会影响到原切片的数据,原因就是append每次拓展都是一个新的空间,指向的内存不再是原切片。
切片append底层原理:
格式:
slice = append(slice,数据)
演示:
func main() {
// 定义了切片的长度是3,初始容量是4,系统会对长度赋上默认值,int类型就是0,所以打印3个0
slice := make([]int, 3, 4)
fmt.Println("初始切片的数据:", slice, "长度:", len(slice))
// 添加数据,此时容量是4,数据已经有4个了,如果继续多加点数据,会不会报错
slice = append(slice, 1)
fmt.Println("第一次添加数据:", slice, "长度:", len(slice))
slice = append(slice, 2, 3, 4)
fmt.Println("第二次添加数据:", slice, "长度:", len(slice))
// slice...:添加slice全部数据
slice = append(slice, slice...)
fmt.Println("第三次添加数据:", slice, "长度:", len(slice))
// 再创建一个切片,初始化后把slice的数据添加过来,注意,这个不是复制,是在结尾继续添加
slice2 := make([]int, 3, 4)
slice2 = append(slice2, slice...)
fmt.Println("slice2:", slice2, "长度:", len(slice2))
}
输出:
初始切片的数据: [0 0 0] 长度: 3
第一次添加数据: [0 0 0 1] 长度: 4
第二次添加数据: [0 0 0 1 2 3 4] 长度: 7
第三次添加数据: [0 0 0 1 2 3 4 0 0 0 1 2 3 4] 长度: 14
slice2: [0 0 0 0 0 0 1 2 3 4 0 0 0 1 2 3 4] 长度: 17
内置函数copy,可以对切片进行拷贝,但切片是引用类型,新切片复制后,修改数据是不会影响原切片的。
copy只是复制索引相对应的数据,覆盖对应索引数据,如果长度不够,不会覆盖原来的数据,
格式:
// 如果切片1容量不够,则不复制剩余数据。如果切片1的数据比切片2多,从切片2复制的数据有多少,复制多少
copy(切片1,切片2) // 把切片2的数据复制到切片1中
从切片2复制到切片1,但是切片2的数据比切片1的多,所以,最终只是复制了一部分,也就是索引相对应的数据
func main() {
slice := []int{1, 2, 3}
slice2 := []int{4, 5, 6, 7, 8, 9}
copy(slice, slice2) // 把切片2复制到切片1
fmt.Println("slice:s", slice)
// 修改数据
slice = append(slice, 97, 98)
slice2 = append(slice2, 100)
fmt.Println("修改数据后:slice中的数据:", slice)
fmt.Println("修改数据后:slice2中的数据:", slice2)
}
输出:
slice:s [4 5 6]
修改数据后:slice中的数据: [4 5 6 97 98]
修改数据后:slice2中的数据: [4 5 6 7 8 9 100]
string底层是一个byte数组,因此string也可以进行切片处理
func main() {
s := "itzhuzhu"
fmt.Println(string(s[0]))
}
切片截取就是从切片中获取指定的数据返回给新切片,新切片与原切片会共用一个底层数组
如果初始化切片
没有指定切片的容量,切片容量是跟随原切片的
切片截取的操作:
操作 | 含义 |
---|---|
s[n] | 从切片s的n索引到len(s)-1 |
s[:] | 从切片s的索引0到len(s)-1 |
s[low:] | 从切片s的索引low到len(s)-1 |
s[:high] | 从切片s的索引0到high,len=high |
s[low:high] | 从切片s的索引low到high处所获得的切片,len=high-low |
s[low:high:max] | 从切片s的索引low到high处所获得的切片,len=high-low,cap=max-low |
len(s) | 切片s的长度,总是<=cap(s),如slice[:len(slice)] |
cap(s) | 切片s的容量,总是>=len(s) |
演示:
func main() {
slice := []int{1, 2, 3, 4, 5}
/**
第一个值:截取的起始索引
第二个值:截取的终止索引(不包括该值)
第三个值:用来计算切片的容量,可以省略,默认和长度一样
容量 = 第三个值 - 第一个值
长度 = 第二个值 - 第一个值
*/
newSlice := slice[0:3:3]
fmt.Println("newSlice:", newSlice, "长度:", len(newSlice), "容量:", cap(newSlice))
// 和复制一样了
newSlice2 := slice[:]
fmt.Println("newSlice2:", newSlice2, "长度:", len(newSlice2), "容量:", cap(newSlice2))
}
输出:
newSlice: [1 2 3] 长度: 3 容量: 3
newSlice2: [1 2 3 4 5] 长度: 5 容量: 5
切片截取后返回新切片,对新切片的值进行修改,会影响原切片
演示:
func main() {
slice := []int{1, 2, 3, 4, 5}
newSlice2 := slice[0:3]
fmt.Println("切片修改前slice的数据:", slice)
newSlice2[0] = 1111
fmt.Println("切片修改后slice的数据:", slice)
}
输出:
切片修改前slice的数据: [1 2 3 4 5]
切片修改后slice的数据: [1111 2 3 4 5]
原因:
切片截取后新的切片是指向了原来的切片,没有给新的切片开辟新的空间,所以对于新的切片操作会影响到原来的切片
新切片与原切片会共用一个底层数组
func main() {
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s := arr[2:5]
// 没有指定切片的容量,则跟随原数组的容量
fmt.Println("切片的长度:", len(s), "\t切片的容量:", cap(s))
fmt.Println("s:", s)
s2 := s[2:8]
fmt.Println("s2:", s2)
fmt.Println("切片的长度:", len(s2), "\t切片的容量:", cap(s2))
}
输出:
切片的长度: 3 切片的容量: 8
s: [3 4 5]
s2: [5 6 7 8 9 10]
切片的长度: 6 切片的容量: 6
切片可以做为函数的参数,但是在函数中修改切片的值,会影响到原切片
func main() {
slice := make([]int, 10)
SliceDemo(slice)
fmt.Printf("main中slice的内存地址:%p\n数据:%v", slice, slice)
}
func SliceDemo(slice []int) {
for i := 0; i < len(slice); i++ {
slice[i] = i
}
fmt.Printf("SliceDemo中slice的内存地址:%p\n", slice)
}
输出:
SliceDemo中slice的内存地址:0x1400001e0f0
main中slice的内存地址:0x1400001e0f0
数据:[0 1 2 3 4 5 6 7 8 9]
使用append就不会影响,而且内存地址都不一样怎么会影响呢?因为append会变成一个新的切片
func main() {
slice := []int{1, 2, 3, 4, 5}
SliceDemo(slice)
fmt.Println("main:", slice)
fmt.Printf("main:%p", slice)
}
func SliceDemo(slice []int) {
slice = append(slice, 6, 7)
fmt.Println("SliceDemo:", slice)
fmt.Printf("SliceDemo:%p\n", slice)
}
输出:
SliceDemo: [1 2 3 4 5 6 7]
SliceDemo:0x1400001c0a0
main: [1 2 3 4 5]
main:0x14000014180
func main() {
// 定义变量,并收集用户输入的个数
var count int
fmt.Println("请输入要求和的个数:")
fmt.Scan(&count)
// 定义切片,将输入的个数保存到切片,count作为切片的长度
slice := make([]int, count)
// 统计求和数据
statisticalData(slice)
// 求和
summation(slice)
}
func statisticalData(slice []int) {
for i := 0; i < len(slice); i++ {
fmt.Printf("请输入第%d个数\n", i+1)
fmt.Scan(&slice[i])
}
}
func summation(slice []int) {
var sum int
for i := 0; i < len(slice); i++ {
sum += slice[i]
}
fmt.Println("和为:", sum)
}
func main() {
// 定义变量,并收集用户输入的个数
var count int
fmt.Println("请输入要比较的个数:")
fmt.Scan(&count)
// 定义切片,将输入的个数保存到切片
slice := make([]int, count)
statisticalData(slice)
// 比较最大值
maximum(slice)
}
func statisticalData(slice []int) {
for i := 0; i < len(slice); i++ {
fmt.Printf("\n请输入第%d个数\n", i+1)
fmt.Scan(&slice[i])
}
}
func maximum(slice []int) {
max := slice[0]
for i := 0; i < len(slice); i++ {
if max < slice[i] {
max = slice[i]
}
}
fmt.Println("\n最大值是:", max)
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。