当前位置: 首页 > news >正文

golang 切片(slice)简单使用

1 golang 切片(slice)简单使用

文章目录

  • 1 golang 切片(slice)简单使用
    • 1.1 slice简介
    • 1.2 slice创建和初始化
    • 1.3 slice操作
      • 1.3.1 slice的访问
      • 1.3.2 slice的遍历
      • 1.3.2 插入元素
      • 1.3.3 删除元素
      • 1.4 slice在流媒体数据处理中使用

在流媒体领域,用golang来进行流媒体服务器的开发,会非常频繁的对二进制数据进行处理,这里slice会经常使用,有关slice的简单使用及注意事项,本文做一个说明。

1.1 slice简介

切片(slice)是Golang中数组之上的抽象;可按需自动增长与缩小,其底层是连续的内存块,切片是包含三个成员变量数据结构,如下所示:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}
  • Data:指向底层数组的指针
  • Len:切片中元素的个数;len(s)获取
  • Cap:切片的容量(不需重新分配内存前,可容纳的元素数量);cap(s)获取

先看下切片定义和初始化的例子:

var (
    a []int               // nil切片,和nil相等,一般用来表示一个不存在的切片
    b = []int{}           // 空切片,和nil不相等,一般用来表示一个空的集合
    c = []int{1, 2, 3}    // 有3个元素的切片,len=3,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=3,cap=3
    h = make([]int, 2, 3) // 有2个元素的切片,len=2,cap=3
    i = make([]int, 0, 3) // 有0个元素的切片,len=0,cap=3
)
j := c[1:2]               //j包含1个元素2,len=2-1,cap=3,其内存还是使用c切片的内存,并未copy

1.2 slice创建和初始化

以建立byte的切片为例,介绍slice创建和初始化过程
var a []byte 定义一个byte的sice,但是未分配任何内存,此时不能直接使用,必须配合make函数进行初始化后才能使用:

var a []byte       //不做任何初始化就会创建一个nil切片
a = make([]byte,0) //空切片,长度和容量都是0,但是不为nil,可以append操作
a = make([]byte,0,100)//切片长度未0,容量未100,空切片
a = make([]byte,3,100) //切片长度为3,初始值为{0,0,0},容量为100

切片长度是切片中实际存入的数据长度,容量是切片初始分配的内存空间,Slice依托数组实现,追加数据通过append(返回值一定要再赋值给原slice),容量不足时会自动扩容,如果容量小于1024,自动扩容为原来2倍,如果容量超过1024,自动扩容为1.25倍,这里一定要注意,如果使用slice进行数据处理时,尽量初始化时分配足够的空间,不要触发切片自动扩容,自动扩容不仅会占用更多的空间,也会存在数据移动操作,消耗资源。

初始化时,也可通过如下几种方式:
a := make([]byte,0,100)
var a []byte = []byte{1,2,3}
a := []int{10: 5} //:=表示变量a类型通过后面表达式推理得到,a第十个元素为5,其他为0,len=cap=11

通过也有切片创建切片,也就是创建切片的子切片,其共享底层内存数组,内存空间使用的就是原始数组的空间,因此在不扩容时修改切片的内容,会影响到其他共享内存的切片:
dst = src[low : high : max]
三个参分别表示:

  • low:为截取的起始下标(含)
  • high:为窃取的结束下标(不含high)
  • max: 为切片保留的原切片的最大下标(不含max)

新切片原始切片的low下标元素为起始,len = high - low, cap = max - low, high 和 max不能超出㡳切片,否则会段错误
如果没有指定max,则max的值为原始切片容量-low。

a := []int{10: 99} //len=cap=11
b := a[1:3] //元素值为{0,0} len=3-1=2,cap=11-1=10
c := a[:5]  //low=0,相当于a[0:5],len=5,cap=11
d := a[8:]  //high=11,len=3,cap=11-8=3
e := a[1:5:5] //len=4 cap=5
f := a[:] //相当于指针赋值,表示同一个切片

1.3 slice操作

主要介绍切片的访问,遍历,插入,删除等操作

1.3.1 slice的访问

可通过下表直接方位,访问不能超过其长度,否则出错:

a := []string{"aaa","bbb","ccc"} //声明并初始化为string的切片,len=cap=3
b := a[3] //b为string类型值为"ccc"
fmt.Println(a[1]) //打印为字符串"bbb"

1.3.2 slice的遍历

切片也是一个集合,通过range遍历元素

a := []string{"aaa", "bbb", "ccc"}
for k, v := range a {
  fmt.Println(k, " ", v) //k为下表,v为值
} 
/*其运行结果为:
0   aaa
1   bbb
2   ccc
*/

注意:

  • range返回的切片值是对应元素的copy,修改不会改变原slice,若要修改则需要通过索引,如a[k] = "xxx"
  • slice是非线程安全的,遍历期间不能对slice进行删除和修改操作,注意要有多线程操作,需要加线程锁sync.RWMutex

1.3.2 插入元素

通过函数append可在切片尾部追加元素,如下:

a := []byte{1,2,3}
a = append(a,1) //在a尾部增加一个byte元素1
a = append(a,[]byte{3,4}...)//在a尾部增加一个元素为3,4的byte切片,切片组合,把[]byte{3.4}切片中元素的值逐个增加到a的尾部
b := []byte{7,8}
a = apend(a,b...)//在a的尾部插入b切片中的元素

注意此种追加方式,如果cap重组,则不会重新分配内存,如果cap不足会触发cap扩容

如果要在切片头部增加元素,则必然会引起内存重新分配与赋值操作,插入方式如下:

a := []byte{1,2,3}
a = append([]byte{5}, a...) //头部必须是与a相同类型的slice,就相当于先把a追加到匿名变量[]byte{1}的尾部,
                            //再把此匿名变量的指针赋值给a,所以a已经不是原来分配的内存空间了
a = append([]byte{1,2,3}, a...)

所以尽量避免在slice头部插入元素,特别是长度加大的slice

还有一种情况,可能要在slice非头部和尾部的地方插入元素,此时会设计数据移动,也可能会使得slice扩容,其方式可通过以下代码实现:

a := []byte{1,2,3}
alen = len(a)
b := []byte{4,5} //插入到a的第一个元素和第二个元素之间
a = append(a, b...)        // 先把长度增加到len(a)+len(b)
copy(a[1+len(b):], a[1:alen])  // 后移len(b)个元素
copy(a[1:1+len(b)], b)

1.3.3 删除元素

删除切片指定元素,Go 标准库并未给出相应的函数,需要我们自己实现,删除的元素若是指针,可能还会被底层数组引用中,从而无法被gc回收,为了能及时的释放,在删除前,先把对应元素设置为nil再删除;
在切片头尾删除时,通过子切片生成的方式最简单,如下方式:

a[len(a)-1]=nil
a = a[:len(a)-1]
a[1] = nil
a = a[1:]

若要删除中间的元素,可通过append移动的方式:

// append追加,覆盖被删除元素
a = append(a[:i], a[i+N:]...)
 
// 复制,覆盖被删除元素
a = a[:i+copy(a[i:], a[i+N:])]

也可以通过遍历的方式来实现,这个可自行实现即可

1.4 slice在流媒体数据处理中使用

在流媒体中会需要对码流数据进行解复用/复用/抽帧/组帧/解码等操作,通过切片的方式能够更加灵活的进行byte数据进行处理,经常会用到切片的插入/拷贝/移动截取等操作,为了更高的数据处理性能,slice使用时需要注意以下方面:

  • slice容量分配,尽可能够用,尽量避免弧线扩容,比如rtp包最大不会超过mtu,我们可以初始化RTP包的slice为稍大于mtu
    rtpbuf := make([]byte, 0, 1480)
  • 尽量使用slice特性截取子slice,不要触发数据拷贝和新内存占用
    payload := rtpbuf[headlen:]
    rtppkt.Timestamp = binary.BigEndian.Uint32(rtpbuf[4:8])
    binary.BigEndian.PutUint32(rtpheader[8:12], p.SSRC)
  • 针对视频流封装,分配内存时可在buf头部预留足够长的预留字节,打包时直接把头字节拷贝到头部,而不触发数据部分的移动或拷贝
buf := make([]byte, 0, 1400+14)
buf = append(buf, []byte{13:0})
data := buf[14:]
header := buf[:14]
...//码流封装和分片
data = append(data,payload...)
//填充头部,例如头部为12个RTPheader
header := buf[2:14]
//rtp头部封装,直接使用header切片生成
...
sendbuf := buf[2:]
//发送数据
...

相关文章:

  • SQL Server Reporting Services
  • 加速迈入云原生时代,国产数据库行业要变天
  • PMP每日一练 | 考试不迷路-9.1(包含敏捷+多选)
  • 一体式城市内涝监测站
  • 【高等数学基础进阶】定积分应用
  • RabbitMQ基本使用一
  • CentOS 7.2 正确安装 MySQL 5.6.35
  • 计算机组成与设计-第五章 memory hierarchy(一)
  • 软考高级系统架构设计师系列论文二:论软件的性能优化设计
  • 【CircuitPython】RaspberryPi Pico RP2040 自定义机械键盘实例
  • CentOS7.6安装Rabbitmq
  • 谁说文艺青年开花店必亏,我用3年时间挣了20万
  • 11种增加访问者在网站上平均停留时间的技巧
  • centos7.6 yum安装 elasticsearch
  • Android字母、数字版本、API级别对照表2022
  • angular2 简述
  • Angular6错误 Service: No provider for Renderer2
  • Angularjs之国际化
  • es6要点
  • Go 语言编译器的 //go: 详解
  • golang中接口赋值与方法集
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • JS专题之继承
  • k8s如何管理Pod
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • Meteor的表单提交:Form
  • Mysql5.6主从复制
  • PaddlePaddle-GitHub的正确打开姿势
  • Puppeteer:浏览器控制器
  • ReactNativeweexDeviceOne对比
  • springMvc学习笔记(2)
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • webpack4 一点通
  • 不发不行!Netty集成文字图片聊天室外加TCP/IP软硬件通信
  • 对JS继承的一点思考
  • 飞驰在Mesos的涡轮引擎上
  • 给Prometheus造假数据的方法
  • ------- 计算机网络基础
  • 将回调地狱按在地上摩擦的Promise
  • 解决iview多表头动态更改列元素发生的错误
  • 来,膜拜下android roadmap,强大的执行力
  • 区块链技术特点之去中心化特性
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 我感觉这是史上最牛的防sql注入方法类
  • 源码安装memcached和php memcache扩展
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • # Apache SeaTunnel 究竟是什么?
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (31)对象的克隆
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (转)机器学习的数学基础(1)--Dirichlet分布