切片
声明切片
var name []T
- T 代表切片类型对应的元素类型
切片默认指向一段连续内存区域,可以是数组,也可以是数组本身
从连续区域生成切片是常规的操作,格式:
slice [开始位置:结束位置]
var a = [3]int{1,2,3}
b := a[1,2]
从数据或切片生成的新的切片具有以下特性:
- 取出的元素数量为:结束位置-起始位置;
- 取出元素不包括结束位置对应的索引,切片最后一个元素你用slice[len(slice)]获取;
- 当缺省开始位置时,表示从连续区域开头到结束位置;
- 当缺生结束位置时,表示从开始位置到整个连续区域末尾;
- 两者同时缺省时,与切片本身等效;
- 两者同时为0,等效于空切片,一般用于切片复位;
- 超界会报错
重置切片,清空拥有的元素
a :=[]int{1,2,3}
fmt.Println(a[0:0)
//[]
make()函数制造切片
make([]T,len,cap)
- len : 长度
- cap : 容量
- 切片不一定必须经过make() 函数才能使用。生成切片、声明后使用append()函数均可以正常使用切片。
append动态添加元素
每个切片会指向一片内存空间,这片空间容纳容量的元素,超过容量,切片就会"扩容"。"扩容"操作往往发生在append调用时。
var car []string
car = append(car,"Old Driver")
//添加多个元素
car = append(car,"ice","Monk")
//添加切片
team := []string{"Pig","Flyingcake","Chicken"}
car = append(car,team...)
fmt.PrintLn(car)
切片的增长规律,参考:https://www.jianshu.com/p/54be5b08a21c
简单的理解如下:
a. 当需要的容量超过原切片容量的两倍时,会使用需要的容量作为新容量。
b. 当原切片长度小于1024时,新切片的容量会直接翻倍。而当原切片的容量大于等于1024时,会反复地增加25%,直到新容量超过所需要的容量。
切片底层是数组逻辑的实现,切片在扩充容量时,会产生一个新数组
为了避免因为切片是否发生扩容的问题导致bug,最好的处理办法还是在必要时使用 copy 来复制数据,保证得到一个新的切片,以避免后续操作带来预料之外的副作用
复制切片元素到另一个切片
copy()函数,可以迅速的讲一个切片的数据复制到另一个切片空间中。
copy( dest Slice, src Slice []T ) int
copy 的返回值表示实际发生复制的元素个数。
package main
import "fmt"
func main() {
//引用切片数据
ref_Data := src_Data
//预分配足够多的元素切片
copy_data := make([]int,element_Count)
//将数据赋值到新的切片空间中
copy(copy_data,src_Data)
//修改原始数据的第一个元素
src_Data[0] = 999
//打印引用切片的第一个元素
fmt.Println(ref_Data[0])
//打印复制切牌呢的第一个和最后一个元素
fmt.Println(copy_data[0],copy_data[element_Count-1])
// 复制原始数据从4到6(不包含)
copy(copy_data,src_Data[4:6])
for i :=0; i < 5; i++ {
fmt.Printf("%d ",copy_data[i])
}
}
/*
999
0 999
4 5 2 3 4
*/
复制空间 独立空间。
从切片中删除元素
GO并没有对删除切片元素提供的专门语法,需要使用切片本身的特性来删除元素。
package main
import "fmt"
func main() {
seq := []string{"a", "b", "c", "d", "e"}
//指定删除位置
index := 2
//查看删除位置之前的元素和之后的元素
fmt.Println(seq[:index], seq[index+1:])
//将删除点前后的元素连接起来
seq = append(seq[:index], seq[index+1:]...)
fmt.Println(seq)
}
Go中删除元素的本质:是以删除元素为分界点,将前后两个部分的内存重新连接起来。
Array类型的值作为函数参数
作为参数传进函数时,传递的是数组的原始值拷贝,此时在函数内部是无法更新该数组的:
// 数组使用值拷贝传参
func main() {
x := [3]int{1,2,3}
func(arr [3]int) {
arr[0] = 7
fmt.Println(arr) // [7 2 3]
}(x)
fmt.Println(x) // [1 2 3] // 并不是你以为的 [7 2 3]
}
如果想修改参数数组:
- 直接传递指向这个数组的指针类型:
// 传址会修改原数据
func main() {
x := [3]int{1,2,3}
func(arr *[3]int) {
(*arr)[0] = 7
fmt.Println(arr) // &[7 2 3]
}(&x)
fmt.Println(x) // [7 2 3]
}
- 直接使用 slice:即使函数内部得到的是 slice 的值拷贝,但依旧会更新 slice 的原始数据(底层 array)
// 会修改 slice 的底层 array,从而修改 slice
func main() {
x := []int{1, 2, 3}
func(arr []int) {
arr[0] = 7
fmt.Println(x) // [7 2 3]
}(x)
fmt.Println(x) // [7 2 3]
}