go切片(常见用法操作与原理)

12 篇文章 0 订阅

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个元素的切片, len2, cap为3
    e = c[0:2:cap(c)]     //2个元素的切片, len2, cap为3
    f = c[:0]             //0个元素的切片, len0, cap为3
    g = make([]int, 3)    //3个元素的切片, len和cap都为3
    h = make([]int, 2, 3) //2个元素的切片, len2, cap为3
    i = make([]int, 0, 3) //0个元素的切片, len0, 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()
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Go语言中,切片(slice)是一个动态数组,它提供了对数组的部分或全部元素的访问和操作功能。切片的底层是通过数组实现的,但与数组相比,切片具有更强大的功能和灵活性。切片的底层实现原理是使用了一个结构体(runtime.slice)来表示切片的结构信息,包括指向底层数组的指针、切片的长度和容量等信息。切片的长度表示切片中实际存储的元素个数,而容量则表示切片底层数组的长度。切片可以根据需要动态扩容,当切片的长度超过了容量时,会重新分配更大的底层数组,并将原来的元素复制到新的底层数组中。这个过程是由Go语言的运行时系统自动完成的,不需要程序员手动操作。使用切片作为参数传递时,性能损耗很小,因为切片作为参数是传递的是一个结构体实例。切片作为参数具有比数组更强大的功能和灵活性,因此在Go语言中推荐使用切片而不是数组作为参数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Golang切片的底层实现原理](https://blog.csdn.net/m0_72560813/article/details/129231704)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值