【新人系列】Golang 入门(四):集合类型 - 上

server/2025/3/15 14:56:54/

✍ 个人博客:https://blog.csdn.net/Newin2020?type=blog
📝 专栏地址:https://blog.csdn.net/newin2020/category_12898955.html
📣 专栏定位:为 0 基础刚入门 Golang 的小伙伴提供详细的讲解,也欢迎大佬们一起交流~
📚 专栏简介:在这个专栏,我将带着大家从 0 开始入门 Golang 的学习。在这个 Golang 的新人系列专栏下,将会总结 Golang 入门基础的一些知识点,并由浅入深的学习这些知识点,方便大家快速入门学习~
❤️ 如果有收获的话,欢迎点赞 👍 收藏 📁 关注,您的支持就是我创作的最大动力 💪

本章节会重点介绍 slice 数组,由浅入深带大家看看 slice 底层是如何实现的。

1. 数组

定义:

//一维数组
var name [count]int

//多维数组

var courseInfo [3][4]string

初始化:

/* 一维数组 */
//方法1
coures1 := [3]string{"go", "grpc", "gin"}//方法2
courses2 := [3]string{2:"gin"}//方法3
courses3 := [...]string{"go", "grpc", "gin"}/* 多维数组 */
var courseInfo [3][4]string//方法1
courseInfo[0] = [4]string{"go", "1h", "bobby", "go学习"}//方法2
courseInfo[0][0] = "go"
courseInfo[0][1] = "1h"
courseInfo[0][2] = "bobby"
courseInfo[0][3] = "go学习"

遍历:

/* 一维数组 */
//方法1
for i := 0; i < len(courses); i++ {fmt.Println(courses[i]}
}//方法2
for _, value := range courses {fmt.Println(value)
}/* 多维数组 */
//方法1
for i := 0; i < len(courseInfo); i++ {for j:=0; j < len(courseInfo[i]); j++ {fmt.Print(courseInfo[i][j] + " ")}fmt.Println();
}//方法2
for _, row := range courseInfo {fmt.Println(row)
}

注意:

  1. [2]string 和 [3]string 是不同的类型
  2. [count]string 是数组,[]string 是切片

2. slice

2.1 定义与初始化

定义:

var courses []string

初始化:

//方法1 - 从数组直接创建 (也可以通过切片创建新的切片)
courses := [5]string{"go", "grpc", "gin", "mysql", "elasticsearch"}
courseSlice := courses[0:2]    //[go, grpc]//方法2 - 使用string{}
courses := []string{"go", "grpc", "gin", "mysql", "elasticsearch"}//方法3 - make
courses := make([]string, 3)
courses[0] = "go"//方法3 - append
var courses []string
courses = append(courses, "go")

注意:
切片数组是左闭右开区间

2.2 常用操作

增加元素:

var courses []string//场景1 - 增加单个元素
courses = append(courses, "go")//场景2 - 增加多个元素
courses = append(coureses, "go", "grpc")//场景3 - 增加切片到尾部
courseSilce1 := []string{"go", "grpc"}
courseSilce2 := []string{"mysql", "es"}
courseSilce1 = append(courseSilce1, courseSilce2...)

删除元素:

courseSlice := []string{"go", "grpc", "mysql", "es", "gin"}
//删除mysql
myslice := append(courseSlice[:2], courseSlice[3:]...)

拷贝:

//浅拷贝 - 切片2改动会影响到切片1
courseSlice2 = courseSlice1[:]//深拷贝 - 切片2改动不会影响到切片1
var courseSlice2 = make([]string, len(courseSlice1))
copy(courseSlice2, courseSlice1)

2.3 底层存储原理

//切片的实际存储类型
type slice struct {array unsafe.Pointer    //用来存储实际数据的数组指针,指向一块连续的内存len   int               //切片中元素的数量cap   int               //array数组的长度
}

在这里插入图片描述
下面我们分场景举两个例子:

  1. 整形

如果我们用 make 来初始化的话,则会分配实际的内存,并且会将元素全部初始化为 0,例如下面我们创建了 5 个大小的空间,但访问的只有前 2 个。

在这里插入图片描述

假设我们现在新增一个元素,并更改其中一个元素的值,可以发现容量没变但实际长度变成了 3,但需要注意的是后面两个元素仍不能访问,否则汇报数组越界的错误。

在这里插入图片描述
2. 字符串

这次我们用 new 来初始化,与 make 不同,new 虽然会创建上面的三部分结构,但并不会分配底层数组空间,所以 data 为 nil。因此,如果我们试图更改数组的值比如 (*ps)[0] = “golang”,则会报错。

在这里插入图片描述

但是,我们可以通过 append 的方式来分配底层数组

在这里插入图片描述

2.4 分配位置

可以发现,我们上面举的例子中 slice 的 data 指针都指向 array 数组的第一个元素,但实际上 slice 可以指向 array 数组的任何一个位置。

注意:
array 数组一旦申明了容量大小就不可改变,因此我们可以通过 slice 来灵活的获得我们想要的数组变量。

来举个例子:

下面需要注意的是,s1 的容量计算范围是从开头到底层数组的结尾,因此其容量为 9。

在这里插入图片描述

注意:
切片是值传递,但是有引用传递的效果,是因为底层都指向同个地方。一旦增加导致扩容,会指向另一块区域。

我们这里拿 s2 切片数组进行模拟,一旦切片数组发生扩容,则会重新分配一块新的内存,此后对该切片数组的操作都不会再影响到原来的底层数组。

在这里插入图片描述

2.5 扩容规则

go1.18 之前:

在这里插入图片描述

// src/runtime/slice.gofunc growslice(et *_type, old slice, cap int) slice {// ...newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap} else {if old.cap < 1024 {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {newcap += newcap / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}}// ...return slice{p, old.len, newcap}
}

go1.18 之后:

在这里插入图片描述

// src/runtime/slice.gofunc growslice(et *_type, old slice, cap int) slice {// ...newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap} else {const threshold = 256if old.cap < threshold {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {// Transition from growing 2x for small slices// to growing 1.25x for large slices. This formula// gives a smooth-ish transition between the two.newcap += (newcap + 3*threshold) / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}}// ...return slice{p, old.len, newcap}
}

举个例子:

func main() {ints := make([]int, 2)fmt.Println(&ints[0], " len: ", len(ints), " cap: ", cap(ints))ints = append(ints, 2, 3, 4)fmt.Println(&ints[0], " len: ", len(ints), " cap: ", cap(ints))
}output
>>> 0xc0000180a0  len:  2  cap:  2
>>> 0xc00000a360  len:  5  cap:  6

按照扩容规则来讲,上面这个例子 2 * 2 < 2 + 3,所以扩容后的容量理应是 5,但是最终的容量却为 6,与规则对不上。

这是因为扩容完之后的容量内存大小可能并不是最终的大小,新分配的内存大小并不是等于预估容量乘以数据类型的大小,而是需要按照一定的内存规格进行匹配。

在许多编程语言中,很多语言中申请分配内存并不是直接与操作系统交涉,而是和语言自身实现的内存管理模块。它会提前向操作系统申请一批内存,分成常用的规格管理起来。我们申请内存时,它会帮我们匹配到足够大且最接近的规格。

在这里插入图片描述
所以按上面这个例子来讲,最终分配的内存为 48 个字节,而在 64 位系统中 int 占 8 个字节,故最终的容量不是 40 / 8 = 5 个字节,而是 48 / 8 = 6 个字节。


http://www.ppmy.cn/server/175186.html

相关文章

2025 比较靠谱的上位机软件开发公司有哪些

上位机作为连接硬件设备与用户操作的核心载体&#xff0c;开发上位机软件则需兼顾高效性、稳定性、可扩展性及行业适配性。从技术能力、服务保障到行业经验&#xff0c;不同企业在细分领域展现出独特优势。带大家了解下2025年比较靠谱且有核心竞争力的上位机软件开发公司有哪些…

Gemini 2.0 Flash 原生图像生成

Google Gemini 2.0 Flash 全新开放原生图像生成功能&#xff0c;为开发者带来了多模态输入、增强推理能力和自然语言理解的全新体验。 多模态输入支持 支持文字与图片的联合输入&#xff08;如&#xff1a;上传产品图输入「将背景换成雪山场景」&#xff09;实现精准的语义理…

谷歌Gemini 2.0 Flash重磅更新:图文融合,初现AGI曙光

Gemini再进化&#xff0c;多模态能力惊艳 谷歌Gemini模型一直以其强大的多模态能力著称。它是一个“水桶型”模型&#xff0c;各项能力均衡&#xff0c;尤其在多模态理解方面处于全球领先地位。近日&#xff0c;谷歌宣布在Google AI Studio和Gemini API上开放Gemini 2.0 Flash的…

微软 System Center Configuration Manager(SCCM)的组件文件

微软 System Center Configuration Manager(SCCM) 或 Microsoft Endpoint Configuration Manager(MECM) 的组件文件,属于企业级设备管理工具的一部分。以下是具体说明: C:\Windows\CCM\smsswd.exe C:\Windows\CCM\tsmanager.exe smsswd.exe 和 tsmanager.exe 是 Micros…

Python网络爬虫之BeautifulSoup库的使用流程和方法

在使用BeautifulSoup解析HTML或XML数据时,需要掌握其基本使用流程和常见方法。本节将详细介绍如何使用BeautifulSoup解析网页,包括加载HTML数据、查找元素、提取文本、获取属性以及遍历HTML结构,帮助读者掌握网页数据解析的核心技能。 1. 使用BeautifulSoup解析HTML数据 在…

编程自学指南:java程序设计开发,数组与集合,为什么需要数组和集合?数组的声明与初始化, 数组遍历,多维数组

编程自学指南&#xff1a;java程序设计开发&#xff0c;数组与集合 学习目标&#xff1a; 掌握数组的声明、初始化和遍历 理解集合框架&#xff08;List、Set、Map&#xff09;的核心区别与应用场景 能够使用集合解决实际数据存储与操作问题 避免数组越界和集合操作中的常见…

如何在Futter开发中做性能优化?

目录 1. 避免不必要的Widget重建 问题&#xff1a;频繁调用setState()导致整个Widget树重建。 优化策略&#xff1a; 2. 高效处理长列表 问题&#xff1a;ListView一次性加载所有子项导致内存暴涨。 优化策略&#xff1a; 3. 图片加载优化 问题&#xff1a;加载高分辨率…

12. Pandas :使用pandas读Excel文件的常用方法

一 read_excel 函数 其他参数根据实际需要进行查找。 1.接受一个工作表 在 11 案例用到的 Excel 工作簿中&#xff0c;数据是从第一张工作表的 A1 单元格开始的。但在实际场景中&#xff0c; Excel 文件可能并没有这么规整。所以 panda 提供了一些参数来优化读取过程。 比如 s…