Go语言必知必会100问题-20 切片操作实战

前言

有很多gopher将切片的length和capacity混淆,没有彻底理清这两者的区别和联系。理清楚切片的长度和容量这两者的关系,有助于我们合理的对切片进行初始化、通过append追加元素以及进行复制等操作。如果没有深入理解它们,缺少高效操作切片的方法,甚至可能导致内存泄露。

切片结构

Go语言中切片的底层是一个数组,也就是说切片中的元素在内存中是连续存储的。如果底层数组元素已满,继续向切片中添加元素,切片会进行扩容操作。在内部实现上,切片包含一个指向底层数组的指针,一个记录数组长度的字段和一个记录数组容量的字段。长度记录的是切片中已添加的元素数量,而容量记录的是数组大小。

切片初始化

下面结合几个具体的程序进行理解。首先来初始化一个给定长度和容量的切片:

s := make([]int, 3, 6)

通过make函数创建切片,它的第一个参数(3)表示切片的长度,是必传参数。第二个参数(6)是非必传参数,该参数表示切片的容量。下图展示了切片s在内存中的分配结果。

在这里插入图片描述

s的底层是一个包含6个元素(容量)的数组,但是因为长度设置为3,所以只初始化了前3个元素。又因为切片中的元素是int类型,所以初始的值为int的类型零值:0. 上图中灰色的格子表示已分配内存但尚未使用。

如果打印切片s的值,得到输出内容是长度范围内的元素值,即[0 0 0]. 如果将s[1]设置为1,则切片中的第二个元素内容会被更新为1,但不会影响切片s的长度和容量。

在这里插入图片描述

访问切片中的元素位置超过切片的长度是不被允许的,将会产生panic, 尽管已经分配的元素个数(切片的容量)比长度大。 例如,下面执行 s[4]=0将会产生pani.

panic: runtime error: index out of range [4] with length 3
append操作

那如何使用剩余灰色的3个空间呢?通过内置的append函数向切片中添加元素。

s = append(s, 2)

可以看到,通过append操作,切片s中添加了一个新的元素2. 元素2存在s中已分配但未使用的空间中(即数组中第4个格子)。如下图所示。此时s的长度变为4.

在这里插入图片描述

如果继续向s添加元素3、4、5, 这个时候元素个数已超过预分配的大小6,此时如何处理呢?

s = append(s, 3, 4, 5)
fmt.Println(s)

执行上面的代码,可以看到输出结果与我们预期的一致,打印 [0 1 0 2 3 4 5].

因为数组是一种大小固定的数据结构,s底层是一个大小为6的数组,所以它只能存储到元素4. 当向里面插入元素5的时候,它已经满了,这时将创建一个新的数组,数组的大小是原来的两倍,然后将原来数组中的元素拷贝到新数组中,最后向新数组中插入元素。处理结果如下图。

在这里插入图片描述

NOTE:在Go语言中,切片在扩容时,新切片的容量大小是旧切片的两倍,直到容量大小为1024,当容量超过1024时,按原来的1.25倍进行扩容。

现在切片s底层关联的是一个新数组,那之前的数组会怎么处理呢?如果它不再被引用,并且是在堆上分配的,则最后将被垃圾回收器(GC)回收。

截取操作

切片截取操作,截取操作的对象是一个数组或切片,从中截取一部分数据,截取的范围是左闭右开区间。下面的代码中,s2是通过截取s1得到的,在内存的结构如下图所示。

s1 := make([]int, 3, 6)
s2 := s1[1:3]

在这里插入图片描述

s1是一个长度为3,容量为6的切片,切片s2是通过s1创建的,两个切片底层引用的是相同的数组。但是,s2下标索引从底层数组的索引1开始,并且容量也与s1不同。如果我们对s1[1]或s2[0]进行更新操作,它们更改的是的底层数组的相同位置值,所以对s1[1]进行更新,将其设置为1,s2[0]的值也同步更新了,此时内存结构如下图所示。

在这里插入图片描述

如果此时执行 s2 = append(s2,2)操作,切片s1会发生变化吗?虽然它们共享的底层数组中的元素已发生变化,第4个格子中的元素被设置为2,但是该索引位置对s1是不可见的,因为它的长度为3, 此时s1和s2在内存中的结构如下。

在这里插入图片描述

现在打印s1和s2的值,输出如下。可以看到,它们的值是不同的,理解这种行为很重要,这样在使用append时就不会做出错误的假设。

s1=[0 1 0], s2=[1 0 2]

NOTE: 切片的底层数组是内部实现,Go开发人员是无法直接操作访问的。唯一的例外是通过对现有数组创建切片。

切片扩容

最后要考虑的一件事是,如果我们不断的将元素append到切片s2中,直到底层数组已满。继续append会产生什么效果?下面通过实验进行说明,继续向s2中append 3个元素.

s2 = append(s2, 3)
s2 = append(s2, 4)
s2 = append(s2, 5)

这样导致会分配一个新的底层数组给s2,因为原来的底层数组已不够容纳元素3、4、5. 此时s1和s2在内存中的结构如下图所示。可以看到,此时s1和s2底层引用的是不同的数组。由于s1是一个长度为3、容量为6的切片,并且它里面装的元素未满,所以继续引用原来的数组。s2新关联新的数组,并且会将s1中与之关联的数据拷贝到新数组中。

在这里插入图片描述

总结

切片长度是切片中已有元素的数量,而切片容量是切片中可以容纳的元素数量。向切片中添加元素,当长度和容量相等时会导致创建具有新容量的新底层数组,复制所有来自前一个数组的元素,并将切片指针更新为新数组。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/446591.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一文彻底搞懂MyISAM和InnoDB区别

文章目录 1. 是否支持行级锁2. 是否支持事务3. 是否支持外键4. 是否支持数据库异常崩溃后的安全恢复5. 是否支持 MVCC6. 索引实现7. 常见的几种 MySQL 存储引擎对比 MySQL 5.5版本之前,MyISAM引擎是MySQL的默认存储引擎,拥有全文索引、压缩和空间函数等特…

力扣——合并k个升序链表

文章目录 题目解析题目链接题目解析 算法讲解暴力解法利用优先级队列进行优化 代码解析 题目解析 题目链接 首先先把题目连接给大家奉上题目链接 题目解析 严格来说这个题目是非常容易理解的相信大家有了合并两个升序链表来理解这个题目就会非常容易理解了。这个题目的意思就…

LeetCode 654.最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点,其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上 构建右子树。 返回 nums 构建的 最大二叉树…

Spring Security | Oauth2 /oauth/token自定义授权实现底层源码浅析与实现

Spring Security Oauth2 /oauth/token自定义授权源码分析实现过程,看了网上很多文章,分析和实现肯定存在不完整地方,可以在评论区指出交流。 1 /oauth/token入口 org.springframework.security.oauth2.provider.endpoint.TokenEndpoint Token…

java的参数传递机制(引用类型)

1.除了非引用类型的形参传递,还有引用类型的变量形参传递,但引用类型的形参变量传递与非引用类型是不同的!!! public class MethodDemo2 {public static void main(String[] args) {int[] arr new int[]{10,20,30,9}…

MySQL入门到中级知识汇总2024

文章目录 1.揭开MySQL的神秘面纱2. SQL的基本命令实操3. 数据库的备份与恢复4. MySQL常用的数据类型(列类型)5. 数据类型之小数类型的使用6. 表的创建7. 表的修改8. mysql事务9. mysql表类型和存储引擎10. mysql的视图11. mysql的管理 1.揭开MySQL的神秘…

20.2 nginx

20.2 nginx 1. 学习目标2. 介绍2.1 正向代理2.2 反向代理2.3 动态静态资源分离2.4 nginx优缺点3. 安装3.1 Linux安装****************************************************************************************************************************************************…

Charles-抓包工具的使用

文章目录 Charles简介与安装Charles的简介Charles的安装Charles 安装证书 抓包在PC端抓取HTTPS请求在移动端进行抓包移动端配置Androd端配置iOS端配置 Charles使用小技巧: 模拟慢速网络 Charles简介与安装 Charles的简介 Charles 是在 PC 端常用的网络封包截取工具…

块设备驱动(1)-什么是块设备驱动?块设备驱动概念总结

1.块设备驱动概念 块设备驱动是针对存储设备,例如SD卡、EMMC、NAND FLASH、NOR FLSASH。 块设备驱动以块为单位进行访问、最小寻址单位是扇区、一个块中包含多个扇区、支持随机访问、带缓冲区,,当发生写入操作时,并不会立马操作硬…

GPU,一统天下

三十年前,CPU 和其他专用处理器几乎处理所有计算任务。那个时代的显卡有助于加快 Windows 和应用程序中 2D 形状的绘制速度,但没有其他用途。 快进到今天,GPU 已经成为业界最具主导地位的芯片之一。 但具有讽刺意味的是,图形芯片…

checking file system on C

1、win7系统 开机检查C盘,虽然可以ESC取消检查,每次操作很麻烦,且没有意思 2、注册表清空BootExecute数值数据 1)打开注册表 WinR (快捷键)输入“regedit”,回车 2)位置HKEY_LOCAL…

HTML5+CSS3+移动web——CSS基础

系列文章目录 HTML5CSS3移动web——HTML 基础-CSDN博客https://blog.csdn.net/ymxk2876721452/article/details/136070953?spm1001.2014.3001.5501HTML5CSS3移动web——列表、表格、表单-CSDN博客https://blog.csdn.net/ymxk2876721452/article/details/136221443?spm1001.2…

基于stm32的流水灯设计

1基于stm32的流水灯设计[proteus仿真] 速度检测系统这个题目算是课程设计和毕业设计中常见的题目了,本期是一个基于51单片机的自行车测速系统设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】,赞赏任意文章 2¥&#xff0c…

考研经验|如何从考研失败中走出来?

对我来说,太丢人了 其实我在本科的时候在同学眼中,一直很优秀,每年奖学金必有我的,国家励志奖学金,国家奖学金,这种非常难拿的奖学金,我也拿过,本科期间学校有一个公费去新西兰留学的…

Haproxy集群

一、HAProxy介绍 HAProxy是法国开发者威利塔罗(Willy Tarreau)在2000年使用C语言开发的一个开源软件,是一款具备高并发(一万以上)、高性能的TCP和HTTP负载均衡器,支持基于cookie的持久性,自动故障切换,支持正则表达式及web状态统…

数据库--SQL语言-1

练习网站:自学SQL网 Select 查询语法复习 SELECT column, another_column, …FROM mytableWHERE condition AND/OR another_condition AND/OR …; 操作符号: 如果属性是字符串, 我们会用到字符串相关的一些操作符号,其中 LIKE&#xff08…

【数理统计实验(四)】方差分析

🍉CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一|统计学|干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项,参与研究经费10w、40w级横向 文…

FIT介绍-0

1、背景 FIT是flattened image tree的简称,它采用了device tree source file(DTS)的语法,生成的image文件也和dtb文件类似(称做itb)。 结构如下图: 其中image source file(.its)和device tree …

Midjourney绘图欣赏系列(八)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子,它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同,Midjourney 是自筹资金且闭源的,因此确切了解其幕后内容尚不…

【alibaba funAsr-0.1.9实时语音转文字部署(内外网】

alibaba funAsr-0.1.9实时语音转文字部署(内外网) 官方参考文档: https://github.com/alibaba-damo-academy/FunASR/blob/main/runtime/docs/SDK_advanced_guide_online_zh.md 前提: 我的内网服务器是华为欧拉,arm…