Go基础学习05-数组和切片关系深度解析

news/2024/10/18 5:53:06/

切片数组的联系

数组(array)和切片(slice)都属于集合类的类型,它们的值也都可以用来存储某一种类型的值(或者说元素)。数组切片最重要的不同在于:

  • 数组类型的值的长度是固定的,而切片类型的值的长度是可变长的。 数组的长度在声明它的时候就必须给定,并且在之后不会再改变。可以说,数组的长度是其类型的一部分。
  • 切片的类型字面量中只有其元素的类型,而没有其长度。切片的长度可以自动地随着其中元素数量的增长而增长,但不会随着元素数量的减少而减少。
  • 切片属于引用类型(切片、通道、函数),数组属于值类型(基础数据类型、结构体类型)。

数组切片的关系:

切片看作是对数组的一层简单的封装(在每个切片底层数据结构中,一定包含一个数组);
数组可以看作切片底层数据结构切片也可以看作是对数组的某个连续片段的引用。

切片的容量和长度的关系:

示例代码1:通过make初始化切片

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

上述代码运行结果:

slice1 len is : 5, cap is : 5
slice2 len is : 5, cap is : 8

当我们使用make函数初始化切片时,如果不指明那个量,那么切片的容量就会和长度一致。如果使用make函数初始化切片时,指明了切片的容量,那么切片的容量就是此时指明的数据,不一定等于切片的长度。通过make函数初始化的切片,其容量代表了切片的底层数组的长度,上述的5和8代表底层数组的长度,数组的长度不可改变。 既然切片底层数据结构数组的长度不可改变,那么当切片容量不足时需要扩容时如果扩?正确的答案:切片容量不足时,会开辟一块更大的空间(足够存储扩容后的所有元素),随后将切片原有元素复制到新的空间,并将新添加的元素追加到后面,随后再将新的引用传递给切片

示例代码2:使用切片表达式基于某个数组切片生成新切片

 	slice3 := []int{1, 2, 3, 4, 5, 6, 7, 8}slice4 := slice3[3:6]fmt.Printf("slice3 elements is : %d\n", slice3)fmt.Printf("slice4 elements is : %d\n", slice4)fmt.Printf("slice3 len is : %d, cap is : %d\n", len(slice3), cap(slice3))fmt.Printf("slice4 len is : %d, cap is : %d\n", len(slice4), cap(slice4))

上述代码执行结果:

slice3 elements is : [1 2 3 4 5 6 7 8]
slice4 elements is : [4 5 6]
slice3 len is : 8, cap is : 8
slice4 len is : 3, cap is : 5

提醒读者特意关注一下第四行的输出结果,可以查看一下是否符合预期。
slice4 := slice3[3 : 6]表示透过切片能看到底层数据结构数组的数据范围,左开右闭。所以表示的就是slice3中元素的索引范围从3到5(不包含6),所以slice4此时输出的元素是456。
此时对于slice4的长度计算就是结束索引 - 起始索引:6-3=3。所以len(slice4)就是3。为什么cap(slice4) = 5,而不是3呢?

使用切片表达式基于数组或者切片生成新的切片,其本质和原数组或者旧切片共用相同的底层数据结构,可以将新切片看作在旧切片的基础上的一个封装,一个拥有左右边界的窗口,左边界是切片表达式的左值,右边界是切片表达式的右值(右边界不能大于原有切片的最大容量,否则panic)。slice4的底层数组就是slice3的底层数组,又因为,在底层数组不变的情况下,切片代表的窗口可以向右扩展,直至其底层数组的末尾。所以,slice4的容量就是其底层数组的长度8减去上述切片表达式中的那个起始索引3,即5。
切片代表的窗口是无法向左扩展的,也就是说,我们永远无法透过slice4看到slice3中最左边的那三个元素

如何估算切片容量的增长

一旦一个切片无法容纳更多的元素,Go 语言就会想办法扩容。但它并不会改变原来的切片,而是会生成一个容量更大的切片,然后将把原有的元素和新元素一并拷贝到新切片中。

  • 在一般的情况下,你可以简单地认为新切片的容量(以下简称新容量)将会是原切片容量(以下
    简称原容量)的 2 倍。
  • 但是,当原切片的长度(以下简称原长度)大于或等于1024时,Go 语言将会以原容量的1.25
    倍作为新容量的基准(以下新容量基准)。新容量基准会被调整(不断地与1.25相乘),直到结果不小于原长度与要追加的元素数量之和(以下简称新长度)。最终,新容量往往会比新长度大一些,当然,相等也是可能的。
  • 此外,如果我们一次追加的元素过多,以至于使新长度比原容量的 2 倍还要大,那么新容量就
    会以新长度为基准。

关于切片底层数组替换的思考

一个切片的底层数组永远不会被替换。虽然在扩容的时候 Go 语言一定会生成新的底层数组,但是它也同时生成了新的切片。它是把新的切片作为了新底层数组的窗口,而没有对原切片及其底层数组做任何改动。

  • 在无需扩容时,append函数返回的是指向原底层数组的新切片,而在需要扩容时,append函数返回的是指向新底层数组的新切片
  • 只要新长度不会超过切片的原容量,那么使用append函数对其追加元素的时候就不会引起扩容。这只会使紧邻切片窗口右边的(底层数组中的)元素被新的元素替换掉。
    代码示例:
	slice3 := []int{1, 2, 3, 4, 5, 6, 7, 8}slice4 := slice3[3:7]fmt.Printf("slice3 elements is : %d\n", slice3)fmt.Printf("slice4 elements is : %d\n", slice4)slice4 = append(slice4, 10)fmt.Printf("slice3 elements is : %d\n", slice3)fmt.Printf("slice4 elements is : %d\n", slice4)

运行结果:

slice3 elements is : [1 2 3 4 5 6 7 8]
slice4 elements is : [4 5 6 7]
slice3 elements is : [1 2 3 4 5 6 7 10]
slice4 elements is : [4 5 6 7 10]

http://www.ppmy.cn/news/1532679.html

相关文章

模拟实现(优先级队列)priority_queue:优先级队列、仿函数、 反向迭代器等的介绍

文章目录 前言一、优先级队列二、仿函数三、 反向迭代器总结 前言 模拟实现(优先级队列)priority_queue:优先级队列、仿函数、 反向迭代器等的介绍 一、优先级队列 优先级队列本质是一个堆,使用vector容器进一步改进进行实现&am…

TFTP协议

目录 一、TFTP协议概述 1.1 TFTP协议简介 1.2 TFTP协议特点 二、TFTP协议原理 2.1 TFTP协议工作流程 2.2 TFTP协议数据包格式 三、TFTP协议应用场景 3.1 网络设备配置文件传输 3.2 虚拟机镜像文件传输 3.3 IoT设备固件升级 四、TFTP协议优化方法 4.1 增加超时重传机…

56 mysql 用户权限相关的实现

前言 这里讨论 mysql 的权限相关处理 使用如下语句创建 tz_test 用户, 并赋予他 test_02 数据库的查询权限 create user tz_test% identified by tz_test; grant select on test_02.* to tz_test%; 查询目标数据表, 数据如下, tz_test_02 UPDATE command denied to user …

QT 界面编程中使用协程

QT 界面编程中使用协程 一、概述二、集成2.1、编译 Acl2.2、将 Acl 库集成到 QT 项目中2.3、开始编写代码2.3.1、QT 程序初始化时初始化 Acl 协程2.3.2、在界面中创建协程2.3.3、界面程序退出前需要停止协程调度2.3.4、在界面线程中下载数据2.3.5、在协程中延迟创建窗口 2.4、效…

HUAWEI WATCH GT 系列安装第三方应用

文章目录 适用机型概述官方文档从源码构建 hap 文件和对源码签名下载和安装DevEco Studio下载和安装首次启动推荐:设置IDE推荐的兼容版本环境(可选)安装并启用中文菜单插件 使用DevEco Studio打开项目并进行构建构建问题解决一、生成密钥和证…

数据分析师之Excel学习

前言 excel作为职场人来说,已经是人人必备的技能了,所以还不知道这个的小伙伴,一定要抓紧时间学习,紧跟时代的步伐。 Excel 几个重要的版本 97-2003版本是国内最早流行的版本 .xlsx后缀的表格文件,基本是07版本及…

Leetcode 968. 监控二叉树 树形dp、状态机 C++实现

问题:Leetcode 968. 监控二叉树 给定一个二叉树,我们在树的节点上安装摄像头。 节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。 计算监控树的所有节点所需的最小摄像头数量。 /*** Definition for a binary tree node.* struct TreeNo…

Linux-基础实操篇-组管理和权限管理(上)

Linux 组基本介绍 在 linux 中的每个用户必须属于一个组,不能独立于组外。在 linux 中每个文件 有所有者、所在组、其它组的概念。 用户和组的基本概念: 用户名:用来识别用户的名称,可以是字母、数字组成的字符串&#xff0…