Go - 4.数组和切片

目录

一.引言

二.定义

1.基础定义

2.引申理解

三.实战

1.估算切片的长度与容量

2.切片的切片长度与容量

四.拓展 

1.估算切片容量的增长

2.切片底层数组的替换

五.总结


一.引言

本文主要讨论 Go 语言的数组 array 类型和切片 slice 类型。主要从二者的使用方法,相同点也不同点进行讲解。

二.定义

1.基础定义

它们的共同点都属于集合类类型,并且,他们的值都可以用来存储某一种类型的元素 T。不过二者最大的不同点是:

        - 数组类型的值长度是固定的

        - 切片类型是可变长的

数组的长度在声明它的时候就必须给定,并且之后不会再改变。可以说,数组的长度是其类型的一部分。比如,[1]string 和 [2]string 就是两个不同的数组类型。

而切片的类型字面量中只有元素的类型,而没有长度。切片的长度可以自动地随着其中元素数量的增长而增长,但不会随着元素数量的减少而减小。

2.引申理解

我们其实可以把切片看做是对数组的一层简单的封装,因为在每个切片的底层数据结构中,一定会包含一个数组。数组可以被叫做切片的底层数组,而切片也可以被看作是对数组的某个连续片段的引用。

也正因为如此,Go 语言的切片类型属于引用类型,同属引用类型的还有字典类型、通道类型、函数类型等;而 Go 语言的数组类型则属于值类型,同属值类型的有基础数据类型以及结构体类型

注意,Go 语言里不存在像 Java 等编程语言中令人困惑的 “传值或传引用” 问题。在 Go 语言中,我们判断所谓的 “传值” 或者 “传引用” 只要看被传递的值的类型就好了。如果传递的值是引用类型的,那么就是 “传引用”。如果传递的值是值类型的,那么就是“传值”。从传递成本的角度讲,引用类型的值往往要比值类型的值低很多。我们在数组和切片之上都可以应用索引表达式,得到的都会是某个元素。我们在它们之上也都可以应用切片表达式,也都会得到一个新的切片。

我们通过调用内建函数 len,得到数组和切片的长度。通过调用内建函数 cap,我们可以得到它们的容量。

Tips:

数组的容量永远等于其长度,都是不可变的。切片的容量却不是这样,并且它的变化是有规律可寻的。

三.实战

1.估算切片的长度与容量

package mainimport "fmt"func main() {// 示例1。s1 := make([]int, 5)fmt.Printf("The length of s1: %d\n", len(s1))fmt.Printf("The capacity of s1: %d\n", cap(s1))fmt.Printf("The value of s1: %d\n", s1)s2 := make([]int, 5, 8)fmt.Printf("The length of s2: %d\n", len(s2))fmt.Printf("The capacity of s2: %d\n", cap(s2))fmt.Printf("The value of s2: %d\n", s2)
}

首先,我用内建函数 make 声明了一个 []int 类型的变量 s1。我传给 make 函数的第二个参数是 5,从而指明了该切片的长度。我用几乎同样的方式声明了切片 s2,只不过多传入了一个参数 8 以指明该切片的容量。现在,具体的问题是:切片s1和s2的容量都是多少?这道题的典型回答:切片 s1 和 s2 的容量分别是 5 和 8。

- 长度与容量的联系

s1 的长度我们声明为 5,因为我们没指明其容量,所以默认与长度一致,容量也是 5,而 s2 指明了容量等于 8。我们顺便通过 s2 再来明确下长度、容量以及它们的关系。我在初始化s2代表的切片时,同时也指定了它的长度和容量。

上面提到可以把切片看做是对数组的一层简单的封装,因为在每个切片的底层数据结构中,一定会包含一个数组。数组可以被叫做切片的底层数组,而切片也可以被看作是对数组的某个连续片段的引用。在这种情况下,切片的容量实际上代表了它的底层数组的长度,这里是8。(注意,切片的底层数组等同于我们前面讲到的数组,其长度不可变。)

2.切片的切片长度与容量

s3 := []int{1, 2, 3, 4, 5, 6, 7, 8}
s4 := s3[3:6]
fmt.Printf("The length of s4: %d\n", len(s4))
fmt.Printf("The capacity of s4: %d\n", cap(s4))
fmt.Printf("The value of s4: %d\n", s4)

切片语法与 python 很像,直接 [] + : 即可,注意这里也是左闭右开区间 [3, 6)。切片 s3 中有 8 个元素,分别是从 1 到 8 的整数。s3 的长度和容量都是 8。然后,我用切片表达式 s3[3:6] 初始化了切片 s4。问题是,这个 s4 的长度和容量分别是多少?

 [3:6] 其实是 [3, 6),这里 3 是起始索引,6 是结束索引不被包含,所以 s4 的长度是 6-3 = 3

再来看容量,切片的容量代表了它的底层数组的长度,但这仅限于使用 make 函数或者切片值字面量初始化切片的情况。 更通用的规则是:一个切片的容量可以被看作是透过这个窗口最多可以看到的底层数组中元素的个数。由于 s4 是通过在 s3 上施加切片操作得来的,所以 s3 的底层数组就是 s4 的底层数组。又因为,在底层数组不变的情况下,切片代表的窗口可以向右扩展,直至其底层数组的末尾。所以,s4 的容量就是其底层数组的长度 8, 减去上述切片表达式中的那个起始索引3,即 5,也就是其实索引后面的长度。

注意,切片代表的窗口是无法向左扩展的。也就是说,我们永远无法透过 s4 看到 s3 中最左边的那 3 个元素。这里可以把切片看作是一个滑动窗口,但是只能向右 ->

- 切片扩充

最后,顺便提一下把切片的窗口向右扩展到最大的方法。对于 s4 来说,切片表达式 s4[0:cap(s4)] 就可以做到。该表达式的结果值(即一个新的切片)会是 []int{4, 5, 6, 7, 8},其长度和容量都是 5。

	s5 := s4[0:cap(s4)]fmt.Printf("The length of s5: %d\n", len(s5))fmt.Printf("The capacity of s5: %d\n", cap(s5))fmt.Printf("The value of s5: %d\n", s5)

四.拓展 

1.估算切片容量的增长

一旦一个切片无法容纳更多的元素,Go 语言就会想办法扩容。但它并不会改变原来的切片,而是会生成一个容量更大的切片,然后将把原有的元素和新元素一并拷贝到新切片中。在一般的情况下,你可以简单地认为新切片的容量(以下简称新容量)将会是原切片容量(以下简称原容量)的 2 倍。

	s6 := make([]int, 0)fmt.Printf("The capacity of s6: %d\n", cap(s6))for i := 1; i <= 5; i++ {s6 = append(s6, i)fmt.Printf("s6(%d): len: %d, cap: %d\n", i, len(s6), cap(s6))}fmt.Println()

但是,当原切片的长度(以下简称原长度)大于或等于 1024 时,Go 语言将会以原容量的 1.5 倍作为新容量的基准(以下新容量基准)。新容量基准会被调整(不断地与 1.5 相乘),直到结果不小于原长度与要追加的元素数量之和(以下简称新长度)。最终,新容量往往会比新长度大一些,当然,相等也是可能的。

	s7 := make([]int, 1024)fmt.Printf("The capacity of s7: %d\n", cap(s7))s7e1 := append(s7, make([]int, 200)...)fmt.Printf("s7e1: len: %d, cap: %d\n", len(s7e1), cap(s7e1))s7e2 := append(s7, make([]int, 400)...)fmt.Printf("s7e2: len: %d, cap: %d\n", len(s7e2), cap(s7e2))s7e3 := append(s7, make([]int, 600)...)fmt.Printf("s7e3: len: %d, cap: %d\n", len(s7e3), cap(s7e3))fmt.Println()

另外,如果我们一次追加的元素过多,以至于使新长度比原容量的 2 倍还要大,那么新容量就会以新长度为基准。注意,与前面那种情况一样,最终的新容量在很多时候都要比新容量基准更大一些。更多细节可参见 runtime 包中 slice.go 文件里的 growslice 及相关函数的具体实现。

	s8 := make([]int, 10)fmt.Printf("The capacity of s8: %d\n", cap(s8))s8a := append(s8, make([]int, 11)...)fmt.Printf("s8a: len: %d, cap: %d\n", len(s8a), cap(s8a))s8b := append(s8a, make([]int, 23)...)fmt.Printf("s8b: len: %d, cap: %d\n", len(s8b), cap(s8b))s8c := append(s8b, make([]int, 45)...)fmt.Printf("s8c: len: %d, cap: %d\n", len(s8c), cap(s8c))

2.切片底层数组的替换

确切地说,一个切片的底层数组永远不会被替换。

虽然在扩容的时候 Go 语言一定会生成新的底层数组,但是它也同时生成了新的切片。它只是把新的切片作为了新底层数组的窗口,而没有对原切片,及其底层数组做任何改动。请记住,在无需扩容时,append 函数返回的是指向原底层数组的原切片,而在需要扩容时,append 函数返回的是指向新底层数组的新切片。所以,严格来讲,“扩容” 这个词用在这里虽然形象但并不合适。不过鉴于这种称呼已经用得很广泛了,我们也没必要另找新词了。

package mainimport "fmt"func main() {// 示例1。a1 := [7]int{1, 2, 3, 4, 5, 6, 7}fmt.Printf("a1: %v (len: %d, cap: %d)\n",a1, len(a1), cap(a1))s9 := a1[1:4]//s9[0] = 1fmt.Printf("s9: %v (len: %d, cap: %d)\n",s9, len(s9), cap(s9))for i := 1; i <= 5; i++ {s9 = append(s9, i)fmt.Printf("s9(%d): %v (len: %d, cap: %d)\n",i, s9, len(s9), cap(s9))}fmt.Printf("a1: %v (len: %d, cap: %d)\n",a1, len(a1), cap(a1))fmt.Println()}

只要新长度不会超过切片的原容量,那么使用 append 函数对其追加元素的时候就不会引起扩容。这只会使紧邻切片窗口右边的(底层数组中的)元素被新的元素替换掉。 这里原始数组窗口为 2,3,4 右边的内容是 5,6,7,由于上面替换的原因,原始数组 a1 的 4,5,6 被替换为 1,2,3:

五.总结

 本文介绍了 Go 语言中的数组和切片类型,我们可以通过 make 的方式初始化数组,也可以通过直接初始化的方法传入元素,同时注意数组扩容期间切片的变化方式。

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

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

相关文章

【开源许可证】介绍

文章目录 概述具体总结 概述 开源许可证通常可以分为两大类&#xff1a;宽松式许可证及 Copyleft 许可证&#xff08;也称著作权&#xff09;。二者的差别主要在于宽松度以及与使用开源软件组件相关的要求和许可权限的多少。 当一个开源组件采用 Copyleft 许可证时&#xff0…

零成本!无需服务器,搭建你的个性化应用!

在快速发展的互联网时代&#xff0c;每个人都有创造自己应用的梦想。但是&#xff0c;传统的应用开发往往需要大量的技术和资源投入&#xff0c;这对于许多独立开发者和初创企业来说是一个巨大的挑战。幸运的是&#xff0c;现在有了 MemFire Cloud&#xff0c;这款无需服务器、…

工业 web4.0 的 UI 卓越非凡

工业 web4.0 的 UI 卓越非凡

前端易遭受的六大安全威胁,以及对应解决策略。

前端遭受安全威胁可能会导致用户隐私泄露、账户被盗用、系统遭受攻击、用户体验受损等严重后果&#xff0c;所有安全防御也成了前端开发者的必须课之一&#xff0c;贝格前端工场带领大家了解下常见的安全威胁。 一、前端开发面临的安全风险 1. 跨站脚本攻击&#xff08;XSS&a…

图形编辑器基于Paper.js教程02:图形图像编辑器概述

背景 由于笔者目前从事开发图形编辑器&#xff0c;在开始的那段时间里&#xff0c;调研和研究了非常多的图形编辑器&#xff0c;图像编辑器之类的软件&#xff0c;开源&#xff0c;闭源的&#xff0c;免费的&#xff0c;商业的都有。今天的这篇文章就来简单概述一下我调研的结…

SpringCloudNetflix组件整合

SpringCloudNetflix组件整合 Eureka注册中心 Eureka是什么 Eureka是netflix的一个子模块&#xff0c;也是核心模块之一&#xff0c;Eureka是一个基于REST的服务&#xff0c;用于定位服务&#xff0c;以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是…

Python实现音乐播放器 -----------内附源码

Python做一个简易的音乐播放器 简易音乐播放器 import time import pygamefile r歌曲路径 pygame.mixer.init() print(正在播放,file) track pygame.mixer.music.load(file) pygame.mixer.music.play() time.sleep(130) pygame.mixer.music.stop()运行效果&#xff1a; 开始…

EE trade:现货黄金的计量单位及转换

在现货黄金市场中&#xff0c;计量单位的不同会影响投资者对价格的理解和对交易的操作。因此&#xff0c;了解现货黄金的计量单位是每一位投资者的必修课。对于那些刚刚踏入黄金投资的新手们来说&#xff0c;掌握这些知识尤为重要。本文将为您详细介绍现货黄金的主要计量单位及…

Harbor本地仓库搭建004_Harbor配置管理功能_分布式分发功能_仓库管理_用户管理_垃圾清理_审查服务_项目定额---分布式云原生部署架构搭建00

然后我们再看一下配置管理,这里主要有个认证模式 这里我们是数据库,其实就是我们安装的postgresql 可以看到还有LDAP对吧,这个其实就是自己公司如果有 LDAP服务器,那么可以对接过来,那么,这个时候 再登录harbor的时候,就可以直接使用公司的,LDAP来管理,所有的用户了,其实就是…

AI项目二十三:危险区域识别系统

若该文为原创文章&#xff0c;转载请注明原文出处。 一、介绍 在IPC监控视频中&#xff0c;很多IPC现在支持区域检测&#xff0c;当在区域内检测到有人闯入时&#xff0c;发送报警并联动报警系统&#xff0c;以保障生命和财产安全具有重大意义。它能够在第一时间检测到人员进入…

Python酷库之旅-比翼双飞情侣库(16)

目录 一、xlwt库的由来 1、背景和需求 2、项目启动 3、功能特点 4、版本兼容性 5、与其他库的关系 6、示例和应用 7、发展历史 二、xlwt库优缺点 1、优点 1-1、简单易用 1-2、功能丰富 1-3、兼容旧版Excel 1-4、社区支持 1-5、稳定性 2、缺点 2-1、不支持.xls…

[创业之路-116] :制造业企业的必备管理神器-ERP-为什么?传统制造业的转型-数字化、智能化下的需求,ERP是管理面和资金面的数字化、智能化的需要

目录 一、时代背景&#xff1a;制造业企业与智能制造 1.1 传统的制造业 1、概念 2、特点 3、面临的挑战&#xff1a;内卷严重 4、发展趋势 1.2 制造业的转型&#xff1a;数字化 1.3 制造业的转型&#xff1a;智能化 1.4 制造业的转型&#xff1a;无人工厂 1、智能化 …

每日一题——8行Python代码实现PAT乙级1029 旧键盘(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 ​编辑我的写法 代码分析 时间复杂度分析 空间复杂度分析 改进建议 方法 1&#…

leetcode33:搜索旋转数组

题目链接&#xff1a;33. 搜索旋转排序数组 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int search(vector<int>& nums, int target) {int n (int)nums.size();if(!n){return -1;}if(n 1){return nums[0] target ? 0 : -1;}int left 0, …

Java开发笔记Ⅲ (一些零碎记录)

一些报错处理 找不到注入的对象 可以在 dao 层 的接口上添加 Repository 注解 common 模块报错 Unable to find main class 由于common中只有一些常量与工具类&#xff0c;不需要主类&#xff0c;故出现该错误时只需删除pom文件中的build标签即可解决 网关模块报错 Failed…

正则表达式常用表示

视频教程&#xff1a;10分钟快速掌握正则表达式 正则表达式在线测试工具&#xff08;亲测好用&#xff09;&#xff1a;测试工具 正则表达式常用表示 限定符 a*&#xff1a;a出现0次或多次a&#xff1a;a出现1次或多次a?&#xff1a;a出现0次或1次a{6}&#xff1a;a出现6次a…

网络安全:探索云安全的最佳实践

文章目录 网络安全&#xff1a;探索云安全的最佳实践引言云安全简介云安全面临的挑战云安全的最佳实践数据加密身份和访问管理定期安全审计 结语 网络安全&#xff1a;探索云安全的最佳实践 引言 在我们之前的文章中&#xff0c;我们讨论了网络安全的多个方面&#xff0c;包括…

2021数学建模A题目–“FAST”主动反射面的形状调节

A 题——“FAST”主动反射面的形状调节 思路&#xff1a;该题主要是通过利用伸缩杆调整FAST反射面&#xff0c;给出合适的调整方案 程序获取 第一题问题思路与结果&#xff1a; 当待观测天体S位于基准球面正上方&#xff0c;结合考虑反射面板调节因素&#xff0c;确定理想抛物…

等保2.0中,如何理解和实施安全管理中心的支持作用?

等保2.0&#xff0c;即《信息安全技术 网络安全等级保护基本要求》的第二版&#xff0c;是中国关于网络安全保护的一项重要标准。它强调了一个中心和三重防护的概念&#xff0c;其中的“一个中心”指的就是安全管理中心&#xff08;Security Management Center,简称SMC&#xf…

代理四川公司疑难商标办理商标异议复审办理

申请商标注册或者办理其他商标事宜&#xff0c;可以自行办理&#xff0c;也可以委托依法设立的商标代理机构办理。外国人或者外国企业在中国申请商标注册和办理其他商标事宜的&#xff0c;应当委托依法设立的商标代理机构办理&#xff0c;按照被代理人的委托办理商标注册申请或…