本节要掌握的问题
- 遍历切片有哪几种方式?如何遍历一个切片?
- 切片初始化的方式有哪几种?有什么区别?
- 切片扩容的底层原理
- copy切片的注意点
- string和slice
一.切片初始化
切片是引用类型,在赋值、值传递等操作时,它不像数组一样需要拷贝数据,因此它的执行效率是高于数组的。
1)变量声明
使用变量声明得到的变量都会初始化为对应类型的零值,因为切片是引用类型,所以初始化为nil。也就是说变量声明得到的切片并不指向任何空间。
2)字面量
s1 := []int{}//空切片
s2 := []int{1,2,3}
通过字面量得到的空切片与变量声明得到的切片有什么区别呢?
如图可以看出使用字面量获得的切片经过了内存分配,其值不再是nil。
s2的长度和容量都是3。字面量声明获得的切片其长度等于容量。
3)使用内置函数make()
- 如果没有给元素赋初始值,则会自动初始化为对应类型的0值。
- 通过make创建的切片对应的数组不可见,由make底层维护。
4)从切片和数组中切取
定义一个数组,然后去引用
var array [5]int = [...]int{1, 2, 3, 4, 5}
var slice = array[1:2]
fmt.Printf("len(slice) = %d\n", len(slice))
fmt.Printf("cap(slice) = %d\n", cap(slice))
此方法得到的切片其长度和容量分别是多少呢?
二、切片注意事项和细节
- 切片初始化时,仍然不能越界。范围在[0-len(slice)]之间,但可以动态增长。例如:
这样会报访问越界的错误。
- 切片如果不标明范围,如:[:],则使用默认值:[0:len(slice)]。
- 切片定义完后还不能使用,因为本身是空的,需要让其引用到一个数组或者make一个空间供切片使用。
否则就会报访问越界的错误。如图:
三、append扩容
var slice []int = []int{1, 2, 3}
//1.直接追加具体的元素
slice = append(slice, 4, 5, 6)
//2.将切片追加给切片
slice = append(slice,slice...)
为什么要将append赋值给slice呢?我们先来看一下append()向slice添加一个元素的实现步骤:
- 如果slice的容量够用,则将新元素追加进去,slice.len++,返回原slice;
- 如果slice的容量不够,则触发扩容操作,扩容实际上是重新分配一块更大的空间,然后将原slice的数据拷贝进新slice。
- 将新元素追加到新slice,slice.len++,返回新的slice。
下列代码输出什么?
s := make([]int, 2, 3)
s[0] = 77
s[1] = 88
s2 := append(s, 99)
s3 := append(s2, 00)
fmt.Println(s)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(&s[0])
fmt.Println(&s2[0])
fmt.Println(&s3[0])
由此可见,append会返回一个新的slice对象,如果原slice的底层数组容量不够用,就会换一个底层数组来使用。
copy
- copy参数的数据类型都是切片。
- 拷贝和被拷贝切片的数据空间相互独立,互不影响。不是说你拷贝后它俩就指向同一块内存空间了。
看一段代码,判断输出是什么:
var a[]int = []int{1,2,3,4,5}
var slice = make([]int,1)
fmt.Println(slice)//[0]
copy(slice,a)
fmt.Println(slice)//[1]
这是由于拷贝过程不会发生扩容,切片拷贝多少由最小的切片决定。
string和slice
- string底层是一个byte数组,因此string也可以进行切片处理。
- string是不可变的。
- 如果非要去修改,可以将string转换成[]byte或者[]rune修改完后转换回string。
第一张图会报错,为什么呢?因为中文字符占三个字节,也就是说arr1[0]能存放一个自己,三个字节的“北”是硬塞不进去的,因此就报错了。
遍历切片
//1.for循环遍历
var arr [5]int = [...]int{1, 2, 3, 4, 5}
slice := arr[1:4]
for i := 0; i < len(slice); i++ {
fmt.Printf("slice[%v]=%v\t", i, slice[i])
}
fmt.Println()
//2.for-range遍历
for i, v := range slice {
fmt.Printf("slice[%v]=%v\t", i, v)
}