开发者学堂课程【Go 语言核心编程 - 基础语法、数组、切片、Map :切片注意事项和细节(2)】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/625/detail/9641
切片注意事项和细节(2)
内容介绍:
一、切片注意事项和细节
二、使用 append 函数进行动态追加的介绍和案例
三、切片的拷贝操作
一、切片注意事项和细节
1.切片注意事项和细节说明:
(1) 切片初始化时 var slice = arr[startIndex:endIndex]
说明:从arr数组下标为 startlndex,取到下标为 endIndex的元素(不含arr[endIndex])。
(2) 切片初始化时,仍然不能越界。范围在[0-len(arr)]之间,但是
以动态增长。
//切片重要特性:动态增长
①var slice = arr[0:end]可以简写var slice = arr[:end]
②var slice = arr[start:len(arr)]可以简写:var slice = arr[start:]
③var slice = arr[0:len(arr)]可以简写:var slice = arr[:]
(3) cap 是一个内置函数,用于统计切片的容量,即最大可以存放
少个元素。
(4) 切片定义完后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者 make 一个空间供切片来使用
(5) 切片可以继续切片
二、使用 append 函数进行动态追加的介绍和案例
用 append 内置函数,可以对切片进行动态追加
1.代码:
v
ar arr [5] int = [5]int {1,2,3,4,5}
v
ar slice = arr [:]
/
/
追加具体的元素
s
lice = append(slice,10,20,30)
f
mt.Println(“slice=”,slice)
//
在切片上追加切片
v
ar a = []int{100,200}
s
lice = append(slice,a…)
fmt.Println(“slice=”,slice)
//当切片数据不够用时,可以进行动态增长,使用的内置函数就是append
2.打开网页,查看 append 函数的作用:
内建函数 append 将元素追加到切片的末尾。若它有足够的容量,其目标就会重新切片以容纳新的元素。否则,就会分配一个新的基本数组。append 返回更新后的切片,因此必须存储追加后的结果。
//append 可以用来扩容
3.切片 append 操作的底层原理分析:
(1) 切片 append 操作的本质就是对数组扩容
(2) go 底层会创建一下新的数组 newArr(安装扩容后大小) //在做 append 底层的时候会创建一个新的数组,将新的数组扩容之后,然后在重新赋给新的切片
(3) 将 slice 原来包含的元素拷贝到新的数组 newArr //将准备追加的新的切片数组先拷贝到新的数组中
(4) slice 重新引用到 newArr
(5) 注意:newArr 是在底层来维护的,程序员不可见。 //slice在底层如何扩容或者创建新的数组,程序员无需关心
(6) 案例演示说明
append 底层原理分析图:
//进行切片时,slice3会引用到新的数组,当执行到 append 时,内存中会先创建一个数组,但是数组是在底层中所创建的我们无法观察。数组在创建过程中,由于会追加三个元素,所以底层会进行扩容。在默认情况下,其实刚生成的数组中的元素的值应该为0。再创建新的数组过程中,是不可能在原先的数组里进行元素追加的,所以要把元素{100,200,300}拷贝至新的数组中,然后新追加的元素会继续赋值到新的数组中,新的数组就形成了,这时,slice3的指针会指向新的数组,而之前的数组就消失了。
由于下方的空间没有被变量引用,所以它将变成垃圾,被系统回收,最终呈如下效果:
假设是追加的 slice4,那么就会创建一个新的 slice4,让其指向新的空间,原先的slice3 仍然没有变化
案例:
var slices3 []int =[]int{100,200,300} //
定义一个切片
fmt.Println(“slice3”,slice) //
将切片进行输出
运行结果:slice3 [100 200 300 ]
(7) 如果要在后方追加新的元素:
第一种方式:
//通过 append 直接给 slice3 追加具体的元素(此元素的值要和上方的数据类型类型保持一致)
slice3 = append(slice3,400,500,600) //追加新的元素
//注意:在追加元素完成后,slice3本身没有变化,在追加的过程中,实际上是内部上进行的分配操作,创建了一个新的数组来进行追加的操作,其实返回的是新的追加完成后的数组,因此需要接收,否则 slice3 还是没有变化。
fmt.Println(“slice3”,slice3) //输出slice3
假设使用 slice4 来进行接收:
fmt.Println(“slice4”,slice4) //slice4是追加过后的元素 {100,200,300,400,500,600}
fmt.Println(“slice3”,slice3) //slice3仍然是之前的元素 {100,200,300}
//实际 slice3 本身没有变化,只是将追加过后的东西重新返回一个结果给新的变量slice4
运行结果如下:
可以发现 slice3 没有发生变化,slice4 发生了变化
需要的效果其实就是改变 slice3,一般情况下只需要将新的变量值重新赋给 slice3 即可:slice3 = append(slice3,400,500,600)
fmt.Println(
“slice3”,
slice3)
/
/slice3
真正意义上实现了扩容
运行结果如下:
slice3 已经是最新的数据
第二种方式:
//在 append 后直接追加一个切片
slice3 = append(slice3,slice3…) //通过 append 函数将切片 slice3 追加给slice3,自我增长
//后面部分的 slice3 也可以是其他的切片,而且此部分必须是切片,不能是数组
//…是规定的写法,必须写上
//{100,200,300,400,500,600, 100,200,300,400,500,600}
fmt.Println(“slice3”,slice3) //重新增加切片后进行输出
运行结果如下:
slice3 在自己的基础上追加了一份slice3
三、切片的拷贝操作
1.切片使用 copy 内置函数完成拷贝
举例说明:
f
unc main() {
v
ar a []int = []int {1,2,3,4,5} //
切片,是单独拥有一个空间
v
ar slice = make([]int , 10)
//make了一个切片,大小为10,在没有赋予值的时候,默认的值都是0
//slice 也有一个独立的空间
fmt.Println(slice) //在没有赋值的情况下,打印出10个0
copy(slice,a) //将a的数据拷贝给 slice,将1,2,3,4,5分别拷贝到对应的位置,后面的值保留原来的值为0
fmt.Println(slice)
}
说明:copy(para1, para2):para1 和 para2 都是切片类型
//注意:拷贝操作必须要求全是切片类型才可以执行拷贝
2.案例:
v
ar slice4 []int = []int{1,2,3,4,5} //
新建一个数组
v
ar slice5
=
make([]int,10) //
使用m
ake
的方式新建一个切片
c
opy(slice5,slice4) //
将s
lice4
拷贝至s
lice5
f
mt.Println(“slice4”,slice4) //
输出1,
2
,
3
,
4
,
5
f
mt.Println(“slice5”,slice5) //
输出1,
2
,
3
,
4
,
5
,
0
,
0
,0,
0
,
0
}
运行效果如下:
slice4 与slice5 都是独立的空间
3. 问题:如果修改slice4会对slice5造成影响吗?
答:不会有影响,因为他们的空间都是独立的,他们之间的数据也是独立的。
4.对代码的说明:
(1) copy(para1, para2)参数的数据类型是切片
(2) 按照上面的代码来看, slice4 和 slice5的数据空间是独立,相互不影响,也就是说 slice4[0]= 999,slice5[0]仍然是1