当前位置: 首页 > 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
  • 【RocksDB】TransactionDB源码分析
  • 03Go 类型总结
  • eclipse(luna)创建web工程
  • FastReport在线报表设计器工作原理
  • JAVA_NIO系列——Channel和Buffer详解
  • Median of Two Sorted Arrays
  • Redux系列x:源码分析
  • Spring核心 Bean的高级装配
  • 那些被忽略的 JavaScript 数组方法细节
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (2)Java 简介
  • (2)STM32单片机上位机
  • (42)STM32——LCD显示屏实验笔记
  • (bean配置类的注解开发)学习Spring的第十三天
  • (搬运以学习)flask 上下文的实现
  • (二)斐波那契Fabonacci函数
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (离散数学)逻辑连接词
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • .form文件_一篇文章学会文件上传
  • .net core使用ef 6
  • .NET Framework 4.6.2改进了WPF和安全性
  • .NET/C# 检测电脑上安装的 .NET Framework 的版本
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .Net程序帮助文档制作
  • .net开发时的诡异问题,button的onclick事件无效
  • .NET应用架构设计:原则、模式与实践 目录预览
  • @Autowired 与@Resource的区别
  • [1]-基于图搜索的路径规划基础
  • [20170705]lsnrctl status LISTENER_SCAN1
  • [BIZ] - 1.金融交易系统特点
  • [C#]获取指定文件夹下的所有文件名(递归)
  • [caffe(二)]Python加载训练caffe模型并进行测试1
  • [CareerCup] 6.1 Find Heavy Bottle 寻找重瓶子
  • [Deepin 15] 编译安装 MySQL-5.6.35
  • [EFI]英特尔 冥王峡谷 NUC8i7HVK 电脑 Hackintosh 黑苹果efi引导文件