Go 切片

切片

一、切片(slice)概念
在讲解切片(slice)之前,大家思考一下数组有什么问题?

  1. 数组定义完,长度是固定的。例如:
var num [5]int = [5]int{1,2,3,4,5}

定义的num数组长度是5,表示只能存储5个整型数字,现在向数组num中追加一个数字,这时会出错。因为你已经定义死了。

  1. 使用数组作为函数参数进行传递时,如果实参为5个元素的整型数组,那么形参也必须5个元素的整型数组,否则出错。

针对以上两个问题,可以使用切片来进行解决。

切片与数组的区别: 切片与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大,所以可以将切片理解成“动态数组”,但是,它不是数组。

二、 切片与数组区别
通过定义,来比较一下切片与数组的区别。

  1. 先回顾数组的基本定义初始化:a := [5]int{},数组中[]是一个固定的数字,表示长度。定义完后,长度是固定,最多存储5个数字。
  2. 切片的基本定义初始化如下:s:=[]int{}//定义空切片看定义的方式,发现与数组很相似.但是注意:切片中的[]是空的,或者是“…”。切片的长度和容量可以不固定。现在通过程序演示,动态向切片中追加数据
/ 初始化切片
s := []int{1,2,3}
// 通过append函数向切片中追加数据
s = append(s,5,6,7)
fmt.Println(s)

append()函数,第一个参数表示向哪个切片追加数据,后面表示具体追加的数据。最终输出结果为

[1 2 3 5 6 7]

三、 切片其它定义方式

  1. 定义空切片:
//声明切片和声明数组一样,只是少了长度,此为空(nil)切片
var s1 []int 
  1. 通过make()函数实现
//借助make函数, 格式 make\(切片类型, 长度, 容量\)
s := make([]int, 5, 10)

四、切片的长度与容量

长度是已经初始化的空间(以上切片s初始空间默认值都是0)。容量是已经开辟的空间,包括已经初始化的空间和空闲的空间。我们可以通过如下图来理解切片的长度与容量:
在这里插入图片描述
该切片的长度是5(存有数据,注意如果没有赋值,默认值都是0),容量是10,只不过有5个空闲区域。即使没有给切片s赋值,初始化的空间(长度)默认存储的数据都是0。

s := make([]int,5,8)
fmt.Println(s)

输出的结果是:

[0 0 0 0 0]

在使用make()函数定义切片时,一定要注意,切片长度要小于容量,例如:

// 以下是错误的
s := make([]int, 10, 5)

make()函数中的容量参数是可以省略掉的,如:

s := make([]int,10)

这时长度与容量是相等的,都是10.
GO语言提供了相应的函数来计算切片的长度与容量,示例如下:

s := make([]int,5,10)
fmt.Println("长度是",len(s))
fmt.Println("容量是",cap(s))

接下来给切片s赋值,可以通过下标的方式直接来进行赋值。如下所示:

s := make([]int,5,10)
s[0] = 1
s[1] = 2

也可以通过循环的方式来进行赋值。

s := make([]int,5,10)
for i:=0;i<len(s) ;i++ {s[i] = i
}

在这里一定要注意,循环结束条件是小于切片的长度,而不是容量。因为,切片的长度是指的是初始化的空间。以下方式会出现异常错误

for i:=0;i<cap(s) ;i++ {s[i] = i
}

给切片赋完值后,怎样将切片中的数据打印出来呢?

  1. 第一种方式:直接通过下标的方式输出,例如:s[0],s[1]…。
  2. 第二种方式: 通过循环的方式,注意循环结束的条件,也是小于切片的长度,如下所示:
for i:=0;i<len(s) ;i++ {fmt.Println(s[i])
}

或者使用range方式输出:

for _,v := range s {fmt.Println(v)
}

四、 切片截取
首先说一下切片的截取操作,所谓截取就是从切片中获取指定的数据。
我们通过如下程序给大家解释一下:

//定义切片 并且完成初始化
s := []int{10,20,30,0,0}//从切片s中截取数据
slice := s[0:3:5]
fmt.Println(slice)

以上程序输出结果:

[10 20 30]

其中s[0:3:5]是什么意思呢?我们来解释一下。每个位置的数字为s[low:high:max]:

  1. 第一个数low表示下标的起点(从该位置开始截取),如果low取值为0表示从第一个元素开始截取,也就是对应的切片s中的10。
  2. 第二个数high表示取到哪结束,也就是下标的终点(不包含该位置),3表示取出下标是0,1,2的数据(10,20,30),不包括下标为3的数据,那么也就是说取出的数据长度是3。可以根据公式:3-0计算(len=high-low),也就是第二个数减去第一个数,差就是数据长度。在这里可以将长度理解成取出的数据的个数。
  3. 第三个数用来计算容量,所谓容量:是指切片目前可容纳的最多元素个数。通过公式5-0计算(cap=max-low),也就是第三个数据减去第一个数。该案例中容量为5。

现在将以上程序进行修改:

//定义切片 并且完成初始化
s := []int{10,20,30,40,50}//从切片s中截取数据
slice := s[0:3:5]
fmt.Println(slice)

结果是:

[10 20 30]

因为起点还是0,也就是10开始,终点还是3也就是到30结束.长度是3,容量是5。

继续修改该程序:

//定义切片 并且完成初始化
s := []int{10,20,30,40,50}//从切片s中截取数据
slice := s[0:4:5]
fmt.Println(slice)

结果是:

[10 20 30 40]

因为起点还是0,也就是10开始,终点还是4也就是到40结束。长度是4,容量是5。
继续修改该程序

//定义切片 并且完成初始化
s := []int{10,20,30,40,50}//从切片s中截取数据
slice := s[1:4:5]
fmt.Println(slice)

slice切片结果是:

[20 30 40]

那么容量是多少呢?容量为4,通过第三个数减去第一个数(5-1)计算。

通过画图的方式来表示slice切片中的容量。
在这里插入图片描述
通过上面的图,可以发现切片s经过截取操作以后,将结果赋值给切片slice后,长度是3,容量是4,只不过有一块区域是空闲的。

切片其他操作。

如下表所示:
在这里插入图片描述
下面通过一个案例,演示一下。

  1. s[:]:
    在这里插入图片描述
    结果是所有的值。

2.s[low:]
在这里插入图片描述
结果是下标3后面的所有值。

  1. s[:high]
    在这里插入图片描述
  2. s[low:high]
    在这里插入图片描述
    结果是2-5的值。array[2:5]表示从下标为2的元素(包含该元素)开始取,到下标为5的元素(不包含该元素)结束。所以切片s5的长度是3。切片s5的容量是多少呢?是8,根据array切片的容量是10,减去array[2:5]中的2。

以上就是关于切片的基本操作,这些操作在以后的开发过程中会经常用到,希望大家记住基本的规律。

五、 思考题

接下来说,思考如下题,定义一个切片array,然后对该切片array进行截取操作(范围自定义),得到新的切片s6,并修改切片s6某个元素的值。代码如下:
在这里插入图片描述
s6切片的结果是:[2,3,4]因为是从下标为2的元素(包含)开始取,到下标为5的元素(不包含)结束,取出3个元素,也就是长度为3。

现在将程序进行如下修改:
在这里插入图片描述
现在程序的输出结果是:

s6 = [2 3 888]

因为切除了234,然后现在0是2,1是3,2是4,然后把s6[2]也就是s6[4]赋值为888
接下来输出切片array的值:
在这里插入图片描述
输出的结果如下:

s6 = [2 3 888]
array = [0 1 2 3 888 5 6 7 8 9]

发现切片array中的值也发生了变化,也就是修改切片s6的值会影响到原切片array的值,下面通过画图的形式来说明其原因。
在这里插入图片描述
在这里重点要理解的是:s6 := array[2:5],将array切片中的array[2],array[3],array[4]截取作为新切片s6,实际上是切片s6指向了原切片array(在这里并不是为切片s6新建一块区域)。所以修改s6,也会影响到array。

下面继续修改上面的程序:
在这里插入图片描述
以上程序中,切片s7的值是多少?

结果是:s7 = [888 5 6 7 8]

下面也是通过画图的形式,来解释该程序的结果:
在这里插入图片描述
继续思考,现在在原有的程序中又加了一行,如下图所示:
在这里插入图片描述
最终,切片s7与原来切片array的值分别是多少?

结果所示:

s6 = [2 3 888]
s7 = [888 5 999 7 8]
array = [0 1 2 3 888 5 999 7 8 9]

六、 append函数的使用

在第一节中,已经给大家讲解过切片与数组很大的一个区别就是:切片的长度是不固定的,可以向已经定义的切片中追加数据。并且也给大家简单的演示过通过append的函数,在原切片的末尾添加元素。

arr := []int{1,2,3}
arr = append(arr,4) //追加一个数
arr = append(arr,5,6,7) //追加多个数
fmt.Println(arr)

如果容量不够用了,该怎么办呢?

例如有以下切片:

s:= make([]int, 5, 8)

定义了切片s,长度是5,容量是8,k。

s := make([]int,5,8)
fmt.Printf("len = %d,cap=%d\n",len(s),cap(s))

结果是:len = 5 cap = 8

并且前面我们讲解过,长度是指已经初始化的空间,现在切片s没有赋值,但是默认值为0
验证如下所示:

s := make([]int,5,8)
fmt.Printf("len = %d,cap=%d\n",len(s),cap(s))
fmt.Println(s)

结果是:

len = 5 cap = 8
[0 0 0 0 0]

现在开始通过append函数追加数据,如下所示:

s := make([]int,5,8)
s = append(s,1)
fmt.Println(s)
fmt.Printf("len = %d,cap=%d\n",len(s),cap(s))

输出结果是:

[0 0 0 0 0 1]
len = 6 cap = 8

从输出的结果上,我们完全能够体会到,append函数的作用是在末尾追加(直接在默认值后面追加数据),由于追加了一个元素,所以长度为6.

但是如果我们把程序修改成如下所示:

s := make([]int,5,8)
//s = append(s,1)
s[0] = 1
fmt.Println(s)
fmt.Printf("len = %d,cap=%d\n",len(s),cap(s))

输出结果是:

[1 0 0 0 0]
len = 5 cap = 8

由于s[0]=1是直接给下标为0的元素赋值,并不是追加,所以结果的长度不变。

下面我们继续通过append( )继续追加数据:

s := make([]int,5,8)
s = append(s,1)
s = append(s,2)
s = append(s,3)fmt.Println(s)
fmt.Printf("len = %d,cap=%d\n",len(s),cap(s))

结果是:

[0 0 0 0 0 1 2 3]
len = 8 cap = 8

追加完成3个数据后,长度变为了8,与容量相同。

那么如果现在通过append( )函数,继续向切片s中继续追加一个数据,那么容量会变为多少呢?

代码如下:

s := make([]int,5,8)
s = append(s,1)
s = append(s,2)
s = append(s,3)
s = append(s,4)
fmt.Println(s)
fmt.Printf("len = %d,cap=%d\n",len(s),cap(s))

输出的结果是:

[0 0 0 0 0 1 2 3 4]
len = 9 cap = 16

追加完成一个数据后,长度变为9,大于创建切片s时的容量,所以切片s扩容,变为16.

那么切片的容量是否是以2倍容量来进行扩容的呢?

我们可以来验证一下:
在这里插入图片描述
输出结果是:
在这里插入图片描述
通过以上结果分析,发现是2倍的容量进行扩容。

但是我们修改一下循环条件看一下结果,将循环结束的条件修改的大一些,如下所示:
在这里插入图片描述
对应的结果:
在这里插入图片描述
通过以上的运行结果分析:当容量小于1024时是按照2倍容量扩容,当大于等于1024就不是按照2倍容量扩容。

七、 copy函数使用

针对切片操作常用的方法除了append()方法以外,还有copy方法。

基本语法:copy(切片1,切片2)

将第二个切片里面的元素,拷贝到第一个切片中。

下面通过一个案例,看一下该方法的使用:
在这里插入图片描述
上面案例中,将srcSlice中的元素拷贝到destSlice切片中。结果如下:

dst = [1 2 6 6 6]

通过以上结果可以分析出,直接将srcSlice切片中两个元素拷贝到dstSlice元素中相同的位置。而dstSlice原有的元素备替换掉。

下面将以上程序修改一下,如下所示:
在这里插入图片描述
以上程序的结果是:

src = [6 6]

通过以上两个程序得出如下结论:在进行拷贝时,拷贝的长度为两个slice中长度较小的长度值。

思考以下程序输出的结果:
在这里插入图片描述
结果是:

slice2 = [1 2 3]

现在将程序进行如下修改:
在这里插入图片描述
结果是:

slice1 = [5 4 3 4 5]

八、切片作为函数参数

切片也可以作为函数参数,那么与数组作为函数参数有什么区别呢?

接下来通过一个案例,演示一下切片作为函数参数。
在这里插入图片描述
通过以上案例,发现在主函数main()中,定义了一个切片s,然后调用InitData()函数,将切片s作为实参传递到该函数中,并在InitData()函数中完成初始化,该函数并没有返回值,但是在主函数中直接打印切片s,发现能够输出对应的值。也就是在InitData()函数中对形参切片num赋值,影响到了main()函数中的切片s。

但是,大家仔细想一下,如果我们这里传递参数不是切片,而是数组,那么能否完成该操作呢?

那么我们将上面的程序,修改成以数组作为参数进行传递的形式:
在这里插入图片描述
发现以数组的形式作为参数,并不能完成我们的要求,所以切片作为函数实参与数组作为函数实参,进行传递时,传递的方式是不一样的。

在GO语言中,数组作为参数进行传递是值传递,而切片作为参数进行传递是引用传递。

九、值传递和引用传递:

  • 值传递:方法调用时,实参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值
  • 引用传递:也称为传地址。函数调用时,实际参数的引用(地址,而不是参数的值)被传递给函数中相对应的形式参数(实参与形参指向了同一块存储区域),在函数执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。

建议:以后开发中使用切片来代替数组。

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

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

相关文章

taro react/vue h5 中的上传input onchange 值得区别

<inputclassNamebase-input-file-h5typefileacceptimage/*capturecameraonChange{onChangeInput} />1、taro3react 2、taro3vue3

javaee idea创建maven项目,使用el和jstl

如果使用el表达式出现下图问题 解决办法 这是因为maven创建项目时&#xff0c;web.xml头部声明默认是2.3&#xff0c;这个默认jsp关闭el表达式 办法1 在每个需要用到el和jstl的页面的上面加一句: <% page isELIgnored"false" %> 方法2 修改web.xml文件开…

回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现CSO-ELM布谷鸟算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介…

Spring Boot进阶(60):5种判断线程池任务是否全部完成的方案 | 实用技巧分享!

1. 前言&#x1f525; 多线程编程在现代软件开发中非常常见且重要&#xff0c;而线程池是多线程编程的常用技术。在使用线程池时&#xff0c;通常需要判断线程池中的任务是否全部完成&#xff0c;以便决定程序继续执行的下一步操作。本文将介绍5种判断线程池任务是否全部完成的…

Android GreenDao数据库升级(附Demo)

前言 大家好久不见&#xff0c;一转眼马上八月份下旬了&#xff0c;最近由于工作比较忙&#xff0c;没时间给大家更新博文。百忙之中抽出时间&#xff0c;给大家来更新一篇关于GreenDao3数据库的升级。 关于GreenDao的详细介绍以及一些逻辑性的增、删、改、查等&#xff0c;可以…

Linux(实操篇一)

Linux实操篇 Linux(实操篇一)1. 常用基本命令1.1 帮助命令1.1.1 man获得帮助信息1.1.2 help获得shell内置命令的帮助信息1.1.3 常用快捷键 1.2 文件目录类1.2.1 pwd显示当前 工作目录的绝对路径1.2.2 ls列出目录的内容1.2.3 cd切换目录1.2.4 mkdir创建一个新的目录1.2.5 rmdir删…

jvm的内存划分区域

jvm划分5个区域&#xff1a; java虚拟机栈、本地方法栈、堆、程序计数器、方法区。 各个区各自的作用&#xff1a; 1.本地方法栈&#xff1a;用于管理本地方法的调用&#xff0c;里面并没有我们写的代码逻辑&#xff0c;其由native修饰&#xff0c;由 C 语言实现。 2.程序计数…

隐秘的角落:Java连接Oracle提示Connection timed out

前言 这个报错相信各位后端开发都不陌生&#xff0c;大体的原因就那么几种&#xff1a; 检查网络连接&#xff1a;确保您的计算机与数据库服务器之间的网络连接正常。尝试通过其他方式验证您的网络连接是否正常。 检查数据库服务器状态&#xff1a;确保数据库服务器正在运行&…

解决redis-server.exe不是内部或外部命令

报错&#xff1a;redis-server.exe不是内部或外部命令 原因&#xff1a;未进入到redis的安装目录下 解决&#xff1a;先找到redis安装路径&#xff0c;复制之后&#xff0c;在终端中输入cd xxxxx(redis的安装路径)&#xff0c;进入安装目录之后再次输入redis-server.exe就成功了…

【腾讯云Cloud Studio实战训练营】React 快速构建点餐页面+Python 拼图小游戏

文章目录 一、腾讯云 Cloud Studio 概述1.1 腾讯云 Cloud Studio 简介1.2 腾讯云 Cloud Studio 功能特点1.3 腾讯云 Cloud Studio 产品优势 二、Cloud Studio界面功能介绍2.1 注册登录2.1.1 新注册用户有免费的3000分钟体验 2.2 界面功能介绍2.2.1 空间模板2.2.2 开发空间关闭空…

android framework之Applicataion启动流程分析

Application启动流程分析 启动方式一&#xff1a;通过Launcher启动app 启动方式二&#xff1a;在某一个app里启动第二个app的Activity. 以上两种方式均可触发app进程的启动。但无论哪种方式&#xff0c;最终通过通过调用AMS的startActivity()来启动application的。 根据上图…

【音视频处理】转编码H264 to H265,FFmpeg,代码分享讲解

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论音视频文件转编码&#xff0c;如将视频H264转H265等。 内容中所提及的代码都会放在GitHub&#xff0c;感兴趣的小伙伴可以到GitHub下载。 我们按这样的顺序展开讨论&#xff1a;​ 1、 编码的作用 2、 转编码的…

npm yarn pnpm npx nvm 命令怎么区分怎么用

npm​​​​​​​ 包管理器&#xff0c;可以用来安装、卸载、更新和管理各种包npm的package.json中文文档 参数 - install&#xff1a;安装一个或多个包。例如&#xff1a;npm install 。 uninstall&#xff1a;卸载一个包。例如&#xff1a;npm uninstall 。 update&#xf…

【stable-diffusion使用扩展+插件和模型资源(下)】

插件模型魔法图片等资源&#xff1a;https://tianfeng.space/1240.html 书接上文&#xff1a;&#xff08;上&#xff09; 插件推荐 1.lobe theme lobe theme是一款主题插件&#xff0c;直接可以在扩展安装 界面进行了重新布局&#xff0c;做了一些优化&#xff0c;有兴趣的…

【业务功能篇85】微服务-springcloud-Nginx-反向代理-网关

Nginx域名 1.hosts文件 在c:/window/system32/drivers/etc/hosts文件&#xff0c;我们在这个文件中添加 192.168.56.100 msb.mall.com注意如果是没有操作权限&#xff0c;那么点击该文件右击属性&#xff0c;去掉只读属性即可 通过这个域名访问到Nginx服务 2.Nginx的方向代…

【C++多线程】C++11互斥锁和条件变量实现生产者消费者模型

先看几个问题&#xff0c;第三个问题可以先看代码然后再理解 Q1&#xff1a;临界区在哪 A1: 队列中元素在「生产者生产&#xff08;push&#xff09;」和「消费者消费&#xff08;pop&#xff09;」时就是临界区 Q2&#xff1a;同步操作在哪 A2: 很显然&#xff0c;队列只有…

基于Java+SpringBoot+Vue前后端分离社区医院管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

若依移动端Ruoyi-App 项目的后端项目入门

后端项目运行 运行报错 Error creating bean with name sysConfigServiceImpl: Invocation of init method failed 数据库创建了。 代码连接数据库地方了也匹配上了。但是还是报错。 分析 &#xff1a; 想起来我电脑从来没有安装过redis 下载安装redis到windows 链接&…

煤质软化:提升采矿效率的关键技术

煤质软化&#xff1a;提升采矿效率的关键技术 随着煤炭的广泛应用和能源需求的增长&#xff0c;对煤炭采矿和利用的要求也不断提高。传统的煤炭采矿方法往往面临一些困难和挑战&#xff0c;如厚层、高硬度以及强抗压性等问题&#xff0c;导致采掘效率低下、操作困难&#xff0c…

4.14 HTTPS 中 TLS 和 TCP 能同时握手吗?

目录 实现HTTPS中TLS和TCP同时握手的前提&#xff1a; 什么是TCP Fast Open&#xff1f; TLS v1.3 TCP Fast Open TLSv1.3 HTTPS都是基于TCP传输协议实现的&#xff0c;得先建立完可靠得TCP连接才能做TLS握手的事情。 实现HTTPS中TLS和TCP同时握手的前提&#xff1a; 1、…