Golang中切片(slice)由于作为存储数据常用容器,而非常重要。对比数组,数组是一个由固定长度的特定类型元素组成的序列。而切片是一种简化版的动态数组,由于长度不固定,因此灵活得多。切片的结构定义,即reflect.SliceHeader:
type SliceHeader struct{
ptr unsafe.Pointer
Len int
Cap int
}
由3部分构成。第一个是指向底层字节数组;
第二个是切片的长度(元素的个数);
第三个表示切片指向的内存空间和最大容量(对应元素的个数,而不是字节数)例如,x := []int{2,3,5,7,11}和y := x[1:3]两个切片对应的内存结构如图所示:
- 切片定义方式:
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 = c[0:2:cap(c)] // 有2个元素的切片, len为2, cap为3
f = c[:0] // 有0个元素的切片, len为0, cap为3
g = make([]int, 3) // 有3个元素的切片, len和cap都为3
h = make([]int, 2, 3) // 有2个元素的切片, len为2, cap为3
i = make([]int, 0, 3) // 有0个元素的切片, len为0, cap为3
)
- 切片使用for循环的三种遍历方式:
for i:= range c{
fmt.Printf("c[%d]:%d\n",i,c[i])
}
for i,v:=range d{
fmt.Printf("d[%d]:%d\n",i,v)
}
for x:=0;x<len(e);x++{
fmt.Printf("e[%d]:%d\n",x,e[x])
}
- 添加切片元素演示:
1.结尾追加
var a_a [] int
//向尾部添加切片
a_a = append(a_a,1) //追加一个元素
fmt.Println("结尾添加元素方式:\n",a_a)
a_a = append(a_a,4,2,3) //追加多个元素,手写解包方式
fmt.Println(a_a)
a_a = append(a_a,[]int{1,2,3}...) //追加一个切片,切片需要解包
fmt.Println(a_a)
2,开头追加
var a_a1 = []int {0}
//在切片开头添加元素。开头添加切片一般会导致内存重新分配,而且会导致已有元素重新复制一次。因此一般性能比尾部追加差很多
a_a1 = append(a_a1,-1,111)
fmt.Println("开头添加元素两种方式:\n",a_a1)
a_a1 = append([]int{-3,-2,-1},a_a1...) //在开头添加一个切片
fmt.Println(a_a1)
3.在中间插入。分别append()函数,append()函数和copy()函数组合方式。
var a_a2 = [] string {"w","a","yu","字"}
//在切片中间插入元素
i:=2
a_a2 = append(a_a2[:i],append([]string{"加入"},a_a2[i:]...)...) //在第i个位置插入"加入"
fmt.Println("在中间加入元素的方式:\n",a_a2)
a_a2 = append(a_a2[:3],append([]string{"yuxuan","siling"},a_a2[3:]...)...)
fmt.Println(a_a2) //在第3个位置加入切片
var a_a3 =[] int {1,2,3,4}
//用copy()和append()组合可以避免创建临时切片,同样是完成添加元素的操作
a_a3 = append(a_a3,0) //切片扩展一个空间
copy(a_a3[i+1:],a_a3[i:]) //a【i:】向后移动一个位置
a_a3[i] = 5 //设置添加新的元素
fmt.Println("copy的方式向中间添加:\n",a_a3)
x:=[] int {6,7,8}
a_a3= append(a_a3,x...) //为x切片扩展足够的空间
copy(a_a3[i+len(x):],a_a3[i:])
copy(a_a3[i:],x)
fmt.Println(a_a3)
- 删除切片元素演示
1.删除尾部
var N int
N = 2
var a_r = [] int {1,2,3,4,5,6}
//尾部删除
a_r = a_r[:len(a_r)-1] //删除尾部1个元素
fmt.Println("从结尾删:\n",a_r)
a_r = a_r[:len(a_r)-N] //再删除尾部N个元素
fmt.Println(a_r)
2.删除开头。三种方式:1.直接移动。2.使用append()不移动指针,而将后面的数据向开头移动。3.使用copy()删除开头的元素
var a_r1 = [] int {12,13,14,15}
//头部删除.删除开头的元素可以直接移动数据指针
a_r1 = a_r1[1:] //删除开头1个元素
fmt.Println("从开头删:\n",a_r1)
a_r1 = a_r1[N:] //再删除开头N个元素
fmt.Println(a_r1)
//也可以不移动数据指针,而将后面的数据向开头移动
a_r2 := [] int {16,17,18,19,20}
a_r2=append(a_r2[:0],a_r2[1:]...) //删一个
fmt.Println(a_r2)
a_r2=append(a_r2[:0],a_r2[N:]...) //删N个
fmt.Println(a_r2)
var a_r3 = [] int {21,22,23,24}
a_r3 = a_r3[:copy(a_r3,a_r3[1:])] //开头删一个
fmt.Println(a_r3)
a_r3 = a_r3[:copy(a_r3,a_r3[N:])] //删N个
fmt.Println(a_r3)
3.删除中间
var a_r5 = [] int {1,2,3,4,5}
//删除中间元素
a_r5 = append(a_r5[:1],a_r5[2:]...) //删除中间第2个元素
fmt.Println("删除中间元素\n",a_r5)
var a_r6 = [] int {40,41,42,43,44}
x:=3
fmt.Println("a_r6切片删除前:",a_r6)
a_r6 = a_r6[:1+copy(a_r6[1:],a_r6[1+x:])] //从第2个位置开始删除3个元素
fmt.Println("删除中间元素后:",a_r6)
4.删除两头
var a_r4 = [] int {30,31,32,33,34}
//删除两头
a_r4 = a_r4[1:4]
fmt.Println("删除两头\n",a_r4)
上述定义切片,添加切片元素,删除切片元素演示部分的代码如下:
package main
import (
"fmt"
)
//定义方式
var (
a []int //nil切片,和nil相等,一般用来表示一个不存在的切片
b = []int{} //空切片,和nil不相等,一般用来表示一个空的集合
c = []int{1,2,3} //有三个元素的切片,len和cap都为3
d = c[:2] //有2个元素的切片,len为2,cap为3
e = c[0:2:cap(c)] //有2个元素的切片,len为2,cap为3
f = c[:0] //有0个元素的切片,len为0,cap为3
g = make([]int,3) //有三个元素的切片,len和cap都为3
h = make([]int,2,3) //有2个元素的切片,len为2,cap为3
i = make([]int,0,3) //有0个元素的切片,len为0,cap为3
)
//切片三种读取方式类似于数组
func read_slice(){
fmt.Println("读取切片的三种方式")
for i:= range c{
fmt.Printf("c[%d]:%d\n",i,c[i])
}
for i,v:=range d{
fmt.Printf("d[%d]:%d\n",i,v)
}
var x int
for x=0;x<len(e);x++{
fmt.Printf("e[%d]:%d\n",x,e[x])
}
}
//append向切片添加元素
func slice_append(){
var a_a [] int
//向尾部添加切片
a_a = append(a_a,1) //追加一个元素
fmt.Println("结尾添加元素方式:\n",a_a)
a_a = append(a_a,4,2,3) //追加多个元素,手写解包方式
fmt.Println(a_a)
a_a = append(a_a,[]int{1,2,3}...) //追加一个切片,切片需要解包
fmt.Println(a_a)
var a_a1 = []int {0}
//在切片开头添加元素。开头添加切片一般会导致内存重新分配,而且会导致已有元素重新复制一次。因此一般性能比尾部追加差很多
a_a1 = append(a_a1,-1,111)
fmt.Println("开头添加元素方式:\n",a_a1)
a_a1 = append([]int{-3,-2,-1},a_a1...) //在开头添加一个切片
fmt.Println(a_a1)
var a_a2 = [] string {"w","a","yu","字"}
//在切片中间插入元素
i:=2
a_a2 = append(a_a2[:i],append([]string{"加入"},a_a2[i:]...)...) //在第i个位置插入"加入"
fmt.Println("在中间加入元素的方式:\n",a_a2)
a_a2 = append(a_a2[:3],append([]string{"yuxuan","siling"},a_a2[3:]...)...)
fmt.Println(a_a2) //在第3个位置加入切片
var a_a3 =[] int {1,2,3,4}
//用copy()和append()组合可以避免创建临时切片,同样是完成添加元素的操作
a_a3 = append(a_a3,0) //切片扩展一个空间
copy(a_a3[i+1:],a_a3[i:]) //a【i:】向后移动一个位置
a_a3[i] = 5 //设置添加新的元素
fmt.Println("copy的方式向中间添加:\n",a_a3)
x:=[] int {6,7,8}
a_a3= append(a_a3,x...) //为x切片扩展足够的空间
copy(a_a3[i+len(x):],a_a3[i:])
copy(a_a3[i:],x)
fmt.Println(a_a3)
}
//Go语言并没有对删除切片元素提供专用的语法或者接口,需要使用切片本身的特性来删除元素,根据要删除元素的位置有三种情况,
//分别是从开头位置删除、从中间位置删除和从尾部删除,其中删除切片尾部的元素速度最快。
func slice_remove(){
var N int
N = 2
var a_r = [] int {1,2,3,4,5,6}
//尾部删除
a_r = a_r[:len(a_r)-1] //删除尾部1个元素
fmt.Println("从结尾删:\n",a_r)
a_r = a_r[:len(a_r)-N] //再删除尾部N个元素
fmt.Println(a_r)
var a_r1 = [] int {12,13,14,15}
//头部删除.删除开头的元素可以直接移动数据指针
a_r1 = a_r1[1:] //删除开头1个元素
fmt.Println("从开头删:\n",a_r1)
a_r1 = a_r1[N:] //再删除开头N个元素
fmt.Println(a_r1)
//也可以不移动数据指针,而将后面的数据向开头移动
a_r2 := [] int {16,17,18,19,20}
a_r2=append(a_r2[:0],a_r2[1:]...) //删一个
fmt.Println(a_r2)
a_r2=append(a_r2[:0],a_r2[N:]...) //删N个
fmt.Println(a_r2)
var a_r3 = [] int {21,22,23,24}
a_r3 = a_r3[:copy(a_r3,a_r3[1:])] //开头删一个
fmt.Println(a_r3)
a_r3 = a_r3[:copy(a_r3,a_r3[N:])] //删N个
fmt.Println(a_r3)
var a_r4 = [] int {30,31,32,33,34}
//删除两头
a_r4 = a_r4[1:4]
fmt.Println("删除两头\n",a_r4)
var a_r5 = [] int {1,2,3,4,5}
//删除中间元素
a_r5 = append(a_r5[:1],a_r5[2:]...) //删除中间第2个元素
fmt.Println("删除中间元素\n",a_r5)
var a_r6 = [] int {40,41,42,43,44}
x:=3
fmt.Println("a_r6切片删除前:",a_r6)
a_r6 = a_r6[:1+copy(a_r6[1:],a_r6[1+x:])] //从第2个位置开始删除3个元素
fmt.Println("删除两边元素后:",a_r6)
}
func main(){
read_slice()
slice_append()
slice_remove()
}