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中与之关联的数据拷贝到新数组中。

在这里插入图片描述

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux安装MeterSphere并结合内网穿透实现公网远程访问本地服务

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…

【Linux】shell理解及linux权限解读(“花花公子Root”的自由人生)

目录 1.shell外壳理解 1.1 什么是shell外壳: 1.2 为什么存在shell外壳程序: 1.3外壳程序的具体工作阶段是怎么样的?(招实习生,工作失败也不影响公司) 2.linux下的权限的概念 2.1linux的用户 2.2.文件类型和…

[天天向上] 学习方法论-事半功倍的问题解决方法

目录 一、尝试独立解决问题1. 关于独立2. 像密室逃脱一样 二、提问的艺术1. 合适的自我介绍1.1 群名片2.2 研究方向/业务内容 2. 详细的问题描述2.1 问题描述要点2.2 描述格式2.3 问题内容描述,尤其是当前进展和问题 3. 如何让更多的人为你解答4. 如何结束提问更优雅…

从零搭建React18.2+ReactRoute6.22+TS5+RTK2.2搭配antd5+antd-style书写All in Js完整体验项目规范

1. 使用CRA创建项目 全局设置npm淘宝镜像源 npm config set registry https://registry.npmmirror.com -g使用最新版create-react-app初始化项目结构 npx create-react-app custom-template --template typescript初始化项目之后在package.json文件中配置使用node>18.0.0…

【AI辅助研发】-趋势:大势已来,行业变革

【AI辅助研发】-趋势:大势已来,行业变革 引言 在科技日新月异的今天,人工智能(AI)技术已逐渐渗透到各行各业,其中软件研发行业更是受益匪浅。AI辅助研发已成为大势所趋,不仅提高了软件开发的效…

算法 环形数组是否存在循环 力扣执行速度击败100%

目录 题目 leetcode 457 求解思路 代码 结果 题目 leetcode 457 存在一个不含 0 的 环形 数组 nums ,每个 nums[i] 都表示位于下标 i 的角色应该向前或向后移动的下标个数: 如果 nums[i] 是正数,向前(下标递增方向&#xff0…

Orange3数据预处理(预处理器组件)

1.组件介绍 Orange3 提供了一系列的数据预处理工具,这些工具可以帮助用户在数据分析之前准备好数据。以下是您请求的预处理组件的详细解释: Discretize Continuous Variables(离散化连续变量): 这个组件将连续变量转…

第3章 数据链路层(1)

3.1数据链路层的功能 加强物理层传输原始比特流的功能,将可能出差错的物理连接改成逻辑上无差错的数据链路[节点的逻辑通道] 3.1.1 为网络提供服务 (1).无确认的无连接服务 适合通信质量好的有线传输链路(实时通信或误码率较低的通信信道)【例如以太网】(2).有确认的无连接服务…

重读 Java 设计模式: 解析单例模式,保证唯一实例的创建与应用

本周工作太忙了,变成了加班狗,下班回来也没时间写,只能利用周末时间写了😭。 好了,言归正传,本次我们先来介绍下设计模式中创建型模式-单例模式。 一、引言 单例模式是设计模式中最简单但又最常用的一种模…

springboot260火锅店管理系统

火锅店管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装火锅店管理系统软件来发挥其高效…

Cloud-Sleuth分布式链路追踪(服务跟踪)

简介 在微服务框架中,一个由客户端发起的请求在后端系统中会经过多个不同的服务节点调用来协同产生最后的请求结果,每一个前端请求都会形成一条复杂的分布式服务调用链路,链路中的任何一环出现高延时或错误都会引起整个请求最后的失败 GitHub - spring-cloud/spring-cloud-sl…

【数据集】waymo motion dataset介绍与数据解析可视化

文章目录 前言1. 数据下载2. Motion数据集3. 数据描述4. 数据特征scenario.proto5. 地图特征map.proto6. 数据解析6.1 数据解析过程6.1 数据解析过程报错7. 数据可视化前言 waymo open dataset数据集由两个数据集组成: Perception数据集 + Motion数据集 Perception数据集主要用…