Golang切片的一些操作
个人博客地址:https://congz666.gitee.io/
定义
定义
var (
a int[] //nil切片,和nil相等,表示不存在的切片
b=[]int{} //空切片,和nil不相等,表示空的集合
c=[]int{1,2,3} //3个元素的切片,len==cap==3
d=c[:2] //2个元素的切片,len==2,cap==3
e=make([]int,3) //3个元素的切片,len==cap==3
f=make([]int,2,3) //2个元素的切片,len==2,cap==3
)
添加切片元素
切片尾部添加
var a []int
a=append(a,1) //追加一个元素
a=append(a,1,2,3) //追加多个元素,手写解包方式
a=append(a,[]int{1,2,3}...) //追加一个切片,切片需要解包
切片头部添加
var a =[]int{1,2,3}
a=append([]int{0},a) //在开头添加一个元素
a=append([]int{1,2,3},a...) //在开头添加一个切片
开头插入会导致内存的重新分配,而且会导致已有的元素全部复制一遍,性能比尾部插入差很多。
切片中间插入
使用多个append()
var a []int
a=append(a[:i],append([]int{x},a[i:]...)...)//在第i个位置插入x
a=append(a[:i],append([]int{1,2,3},a[i:]...)...)//在第i个位置插入切片
缺点:每个添加操作中的第二个append()调用都会创建一个临时切片
改进:append()和copy()组合
插入一个元素
a=append(a,0) //切片扩展一个空间
copy(a[i+1:],a[i:]) //a[i:]向后移动一个位置
a[i]=x //设置新添加的元素
插入多个元素
a=append(a,x...) //切片扩展x个空间
copy(a[i+len(x):],a[i:]) //a[i:]向后移动len(x)个位置
copy(a[i:],x) //复制新添加的切片
删除与以上同理
切片内存技巧
//删除[]byte中的空格
func TrimSpace(s []byte) []byte {
b :=s[:0] //获取len==0,cap!=0的切片
for _, x:=range s {
if x !=' ' {
b=append(b,x)
}
}
return b
}
切片高效操作的要点是要降低内存分配的次数,尽量保证append()操作不会超出cap的容量,降低触发内存分配的次数和每次分配内存的大小
切片内存管理
有时候可能会因为一个小的内存引用而导致底层整个数组处于被使用的状态,这会延迟垃圾回收器对底层数组的回收,例如:
var a []*int{ ... }
a=a[:len(a)-1] //被删除的最后一个元素依然被引用,可能导致垃圾回收器操作被阻碍
保险的方式是先将指向需要提前回收内存的指针设置为nil
var a[]*int{ ... }
a[len(a)-1] = nil //垃圾回收器回收最后一个元素内存
a = a[:len(a)-1] //从切片删除最后一个元素
如果切片存在周期很短的话,就不用刻意处理这个问题。
切片类型强制转换
第一种
先将切片数据的开始地址转换为一个较大的数组的指针,然后对数组指针对应的数组重新做切片操作。中间需要unsafe.Pointer
来连接两个不同类型的指针传递。
var a=[]float64{4,2,5,7,2,1,88,1}
var b []int=((*[1 << 20]int)(unsafe.Pointer(&a[0])))[:len(a):cap(a)]
第二种
分别取两个不同类型的切片头信息指针,任何类型的切片头部信息底层都对应reflect.SliceHeader
结构,然后通过更新结构体方式来更新切片信息。
var a=[]float64{4,2,5,7,2,1,88,1}
var c[]int
aHdr :=(*reflect.SliceHeader)(unsafe.Pointer(&a))
cHdr :=(*reflect.SliceHeader)(unsafe.Pointer(&c))
*cHdr = *aHdr