Go语言学习Day6:数组与切片

名人说:莫愁千里路,自有到来风。 ——钱珝
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)

目录

      • 1. 数组
        • ① 什么是数组
        • ② 数组的声明
        • ③ 初始化数组的几种方式
        • ④ 遍历数组元素
        • ⑤ 数组为值类型
        • ⑥ 数组的排序
        • ⑦ 多维数组
      • 2. 切片(动态数组的抽象)
        • ① 定义切片
        • ② make来创建切片
        • ③ 切片扩容
        • ④ 遍历切片
        • ⑤ 扩容的内存分析
      • 3、小结

Go语言中的数组和切片是管理集合数据的重要方式,具有不同的特性和用途。在这篇博客中,我们将深入探讨数组和切片的相关知识点,包括它们的定义、操作方法及其内部机制。

1. 数组

① 什么是数组

数组是同一种数据类型元素的集合。在Go语言中,数组是值类型,数组的长度在声明时就已经确定,且不能改变。关于数组可以这样理解,有一个收纳盒,专门用于收集同一类书的,但是这个收纳盒容量是有限的,自从这个收纳盒生产出来大小就已经确定了,而且不能再改变,而这个收纳同一类型的盒子也就是数组。

② 数组的声明

数组声明的基本语法格式为:

var arr [n]Type

其中n表示数组的长度,Type表示数组中元素的类型。

案例:数组的定义、赋值、打印

package mainimport "fmt"/*
数组:相同类型数据的集合
*/// 数组
func main() {//array 数组定义,变量//数组也是一种数据类型//数组的定义:[数组的大小size] 变量的类型//意思为:定义一组该类型的数组集合,大小为size,最多可以保存size个数var arr1 [5]int//[0,0,0,0,0]//给数组赋值,下标index从0开始arr1[0] = 100arr1[1] = 200arr1[2] = 300arr1[3] = 400arr1[4] = 500//打印数组fmt.Println(arr1)//取出数组中的某个元素fmt.Println(arr1[1])//数组中常用的方法 len()获取数组长度 cap()获取数组容量fmt.Println("数组的长度:", len(arr1))fmt.Println("数组的容量:", cap(arr1))//修改数组的值,index 1 代表数组中的第二个数据arr1[1] = 50fmt.Println("修改后的数组:", arr1)fmt.Println(arr1[1])
}

image-20240327172446685

③ 初始化数组的几种方式
  • 指定所有元素初始化
arr := [5]int{1, 2, 3, 4, 5}
  • 部分元素初始化,未初始化元素为零值
arr := [5]int{1, 2} // 其余元素初始化为0
  • 根据初始化值自动确定数组长度
arr := [...]int{1, 2, 3, 4, 5}

案例:数组初始化

package mainimport "fmt"// 数组的赋值初始化
func main() {//定义时初始化var arr1 = [5]int{1, 2, 3, 4, 5}fmt.Println(arr1)//快速初始化 :=arr2 := [5]int{1, 2, 3, 4, 5}fmt.Println(arr2)//需要注意的点//数据假如来自用户, 但不知道究竟有多少数据//... 写法//Go的编译器会根据数组的长度来给 ...赋值,自动推导长度//注意点:数组不是无限长,也是固定大小,大小取决于数组元素的个数var arr3 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}fmt.Println(len(arr3))fmt.Println(arr3)//数组默认值,给其中的几个位置赋值//可使用{index:值}的方法var arr4 [10]intarr4 = [10]int{1: 100, 5: 500}fmt.Println(arr4) // [0 100 0 0 0 500 0 0 0 0]
}

image-20240329001959507

④ 遍历数组元素

遍历数组元素可以使用for循环或for range循环。

arr := [...]int{1, 2, 3, 4, 5}
for i := 0; i < len(arr); i++ {fmt.Println(arr[i])
}

或者

for index, value := range arr {fmt.Printf("Index: %d, Value: %d\n", index, value)
}

上面那种方式大家可能比较熟悉,C、C++等语言中都是类似的写法,但是下面这种方式如果没学过python等语言,可能了解会少一些,因此在个人在这里解释一下:

这段代码实现了什么?

for循环遍历arr数组(或切片)。

这段代码该怎么理解?

可以这样看,每次迭代,for循环会将当前元素的索引赋值给index变量,将当前元素的值赋值给value变量,然后执行循环体内的语句。而其中的range arr就是负责生成索引和对应值的部分,比如在循环刚开始,数组中index=0,arr[index] = 1,那么所生成的index值 = 0,value值 = arr[index] = 1。

让我们逐步解析这段代码:

  1. for index, value := range arr { ... }

    • for关键字开始一个循环。
    • index, value是每次迭代时,range表达式返回的两个值。index是当前元素的索引,value是当前元素的值。
    • range arr表示对arr进行遍历。arr可以是数组或切片。range每次迭代都会返回当前元素的索引和值。
    • { ... }中的代码是循环体,会对arr中的每个元素执行一次。
  2. fmt.Printf("Index: %d, Value: %d\n", index, value)

    • 这行代码在循环体内,使用fmt.Printf函数打印当前元素的索引和值。%d是格式化字符串,用于表示整数。\n是换行符。
    • 在每次迭代中,indexvalue将被设置为当前遍历到的元素的索引和值,然后将它们打印出来。

举个具体的例子,如果arr是一个包含三个元素的切片[10, 20, 30],那么输出将会是:

Index: 0, Value: 10
Index: 1, Value: 20
Index: 2, Value: 30

因此,一言以蔽之,这段代码的作用就是遍历数组(或切片)arr,并打印出每个元素的索引和值。

⑤ 数组为值类型

数组在Go语言中是值类型,当数组作为函数参数传递时,传递的是数组的副本而不是其引用。

func modifyArray(arr [5]int) {arr[0] = 10 // 这里修改的是数组的副本
}

案例:数组,值类型

 package mainimport "fmt"// 数组是值类型:所有赋值后的对象修改值不影响原来的对象func main() {//数组 [size]typearr1 := [4]int{1, 2, 3, 4}arr2 := [5]string{"yueliusu", "zilihuakai"}fmt.Printf("%T\n", arr1) //[4]intfmt.Printf("%T\n", arr2) //[5]string//数组的值传递和int等基本类型一致arr3 := arr1fmt.Println(arr1)fmt.Println(arr3)//修改arr3观察arr1是否会变化arr3[0] = 12fmt.Println(arr1)fmt.Println(arr3) // 数组是值传递,拷贝一个新的内存空间}

image-20240328001449100

⑥ 数组的排序

可以自定义实现,也可以使用sort包可以对数组进行排序。例如,对一个整型数组进行升序排序:

package main
import "fmt"
import "sort"func main() {arr := [5]int{3, 1, 4, 5, 2}sort.Ints(arr[:]) // 将数组转换为切片fmt.Println(arr)
}

image-20240329121103889

案例:自己实现排序算法,冒泡排序

package mainimport "fmt"// 冒泡:每次筛选出一个最大或者最小的数.
/*
index   0   1   2   3   4
value   12  99  79  48  55
*/
// 冒泡排序逻辑,两两比较,大的往后移或者前移。 大
// 第一轮 : 12 79 48 55 99 // 5
// 第二轮 : 12 48 55 79 99 // 4
// 第三轮 : 12 48 55 79 99 // 3 //
// 第四轮 : 12 48 55 79 99 //
// 第五轮 : 12 48 55 79 99// 代码实践
/*// 两个数判断,如果一个数大,则交换位置,大放到后面if arr[x] > arr[x+1] {arr[x], arr[x+1] = arr[x+1],arr[x]}// 多轮判断,for, 循环次数 【数组大小】
*/
func main() {arr := [...]int{12, 99, 79, 48, 55, 1, 110, 111, 23, 44, 21, 312, 123, 21, 312}fmt.Println("初始数组:", arr)//冒泡排序//1、多少轮for i := 1; i < len(arr); i++ {//2、筛选出来最大数for j := 0; j < len(arr)-i; j++ {//降序排序,比较大小,改变升降序只需要改变符号即可if arr[j] < arr[j+1] {arr[j], arr[j+1] = arr[j+1], arr[j] //平行赋值,无需第三者临时变量进行交换}}}fmt.Println(arr)
}

image-20240329002137133

⑦ 多维数组

多维数组可以通过嵌套数组来声明(俗称套娃)。

一维,线性

二维,表格,数组套数组

三维,立体,数组套数组套数组

多维,继续嵌套

var multiArr [2][3]int
multiArr[0] = [3]int{1, 2, 3}
multiArr[1] = [3]int{4, 5, 6}

案例:多维数组

package mainimport ("fmt"
)func main() {//多维数组//定义一个二维数组arr := [3][4]int{{0, 1, 0, 0}, //arr[0]{0, 0, 1, 0}, //arr[1]{0, 2, 0, 0}, //arr[2]}//二维数组fmt.Println(arr[0])fmt.Println(arr[0][1])fmt.Println("----------")//遍历二维数组for i := 0; i < len(arr); i++ {for j := 0; j < len(arr); j++ {fmt.Println(arr[i][j])}}// for rangefor i, v := range arr {fmt.Println(i, v)}
}

image-20240329002342053

2. 切片(动态数组的抽象)

① 定义切片

切片是对数组的封装,它提供了一个可动态扩展的序列。切片不存储任何数据,它**只是对底层数组的引用。**那么它与数组相同吗?

显然,切片与数组是不同的,切片的长度是动态的,可以根据需要扩展或缩减。切片背后实际上是对数组的引用切片是引用类型。

var slice []int

补充一下关于切片的基础概念:

  • 底层数组:每个切片都指向一个底层的数组,切片通过指针引用数组的一部分元素。
  • 长度(Length):切片的长度是它所包含的元素个数。可以通过内置的len函数获取。
  • 容量(Capacity):切片的容量是从其起始元素到底层数组末尾元素的个数。可以通过内置的cap函数获取。

案例:切片的定义

package mainimport "fmt"// 定义切片
func main() {arr := [4]int{1, 2, 3, 4} //数组,定长fmt.Println(arr)var s1 []int //切片,不定长,长度是可变的fmt.Println(s1)//切片空片段,初始的切片中,默认为nilif s1 == nil {fmt.Println("切片是空的")}s2 := []int{1, 2, 3, 4} //切片 不定长fmt.Println(s2)fmt.Printf("%T,%T\n", arr, s2) //[4]int,[]intfmt.Println(s2[1])
}

image-20240329122336156

② make来创建切片

通过make函数创建切片,可以指定切片的长度和容量。

//例如,创建一个长度为5,容量为10的切片
slice := make([]int, 5, 10)

案例:make创建切片

package mainimport "fmt"func main() {//make()//make([]Type, length, capacity) //创建一个切片,长度,容量s1 := make([]int, 5, 10)fmt.Println(s1)fmt.Println(len(s1), cap(s1))//思考:容量为10,长度为5,可以存放6个数据吗?s1[0] = 10//s1[7] = 200 //runtime error: index out of range [7] with length 5//切片的底层还是数组 [0 0 0 0 0] [2000]//直接去赋值是不行的,不要用常规的惯性想法来考虑,不加思索说出答案,思考的过程很重要。fmt.Println(s1)}

image-20240329123232663

③ 切片扩容

当向切片追加元素超过其容量时,Go会自动进行扩容,一般会翻倍增加,比如说原本容量为2,你要放3个,放不下了,它会自动扩容为4,依次类推,扩容为8、16等

slice := make([]int, 0, 2)
for i := 0; i < 10; i++ {slice = append(slice, i)
}

案例:切片扩容,自动翻倍

package mainimport "fmt"func main() {//这段代码意味着创建了一个整型切片,初始长度为0,容量为5s1 := make([]int, 0, 5)fmt.Println(s1)//切片扩容,append()s1 = append(s1, 1, 2)fmt.Println(s1)//问题:容量只有5个,那能放超过5个的吗?当然,切片是会自动扩容的s1 = append(s1, 2, 3, 4, 5, 6, 7, 8, 9)fmt.Println(s1)s2 := []int{100, 200, 300, 400}// slice = append(slice, anotherSlice...)// ... 可变参数 ...xxx// [...] 根据长度变化数组的大小定义// anotherSlice... , slice...解构,可以直接获取到slice中的所有元素// s2... = {100,200,300,400}s1 = append(s1, s2...)//遍历切片for i := 0; i < len(s1); i++ {fmt.Println(s1[i])}for i := range s1 {fmt.Println(s1[i])}}

image-20240329125405692

④ 遍历切片

切片的遍历方式和数组相同,可以使用for循环或for range循环,因为本质上切片相当于是引用了数组,数组是一种数据类型,切片引用它,扩展了它,因此遍历方式没太大差别。

slice := []int{1, 2, 3, 4, 5}
for _, value := range slice {fmt.Println(value)
}
⑤ 扩容的内存分析

切片的扩容会导致内存重新分配及旧数据的复制,这可能影响性能。合理规划初始容量可以减少扩容的次数,提升性能。

slice := make([]int, 0, 1024) // 提前分配足够的容量

案例:切片扩容的内存变化

package mainimport "fmt"// 切片扩容的内存分析
// 结论
// 1、每个切片引用了一个底层的数组
// 2、切片本身不存储任何数据,都是底层的数组来存储的,所以修改了切片也就是修改了这个数组中的数据
// 3、向切片中添加数据的时候,如果没有超过容量,直接添加,如果超过了容量,会自动扩容,成倍增加,copy
// - 分析程序的原理
// - 看源码
// 4、切片一旦扩容,就是重新指向一个新的底层数组func main() {//1、cap每次是成倍增加的//2、只要容量扩容后,地址就会发生变化s1 := []int{1, 2, 3}fmt.Println(s1)fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:3,cap:3fmt.Printf("%p\n", s1)                          //0xc000016108s1 = append(s1, 4, 5)fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:5,cap:6fmt.Printf("%p\n", s1)                          //0xc000010390s1 = append(s1, 6, 7, 8)fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:8,cap:12fmt.Printf("%p\n", s1)                          //0xc00005e060s1 = append(s1, 9, 10)fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:10,cap:12fmt.Printf("%p\n", s1)                          //0xc00005e060s1 = append(s1, 11, 12, 13, 14)fmt.Printf("len:%d,cap:%d\n", len(s1), cap(s1)) //len:14,cap:24fmt.Printf("%p\n", s1)                          //0xc00010c000
}

image-20240329125625358

案例2:copy方法

package mainimport "fmt"// copy方法
func main() {numbers := []int{1, 2, 3}fmt.Printf("len=%d,cap=%d,slice=%v\n", len(numbers), cap(numbers), numbers)//方法一:直接使用make创建切片扩容numbers2 := make([]int, len(numbers), cap(numbers)*2)//将原来的底层数据的值拷贝到新的数组中//func copy(dst,src []Type)intcopy(numbers2, numbers)fmt.Printf("len=%d,cap=%d,slice=%v\n", len(numbers2), cap(numbers2), numbers2)
}

image-20240329125643799

3、小结

数组

  • 一组数
  • 数组是值传递的
  • 创建数组 [size]int
  • 数组的大小事不可变的
  • 二维数组,数组套数组
  • 冒泡排序

切片

  • 切片本事是不存在数据的
  • 底层是指向了一个数组
  • 如果我们存放的数据大于切片的容量,在底层就会扩容 copy > 1024 1.25倍

很感谢你能看到这里,如有相关疑问,还请下方评论留言。
Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
希望本篇内容能对大家有所帮助,如果大家喜欢的话,请动动手点个赞和关注吧,非常感谢你们的支持!

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

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

相关文章

Excel 粘贴回筛选后的单元格不能完全粘老是少数据 ,有些单元格还是空的

环境&#xff1a; excel2021 Win10专业版 问题描述&#xff1a; excel 粘贴回筛选后的单元格不能完全粘老是少数据 有些单元格还是空的 复制选择筛选后A1-A10单元格 &#xff0c;定位条件&#xff09;&#xff08;仅可见单元格&#xff09;来访问&#xff0c;或者你可以使用…

牛角工具箱源码 轻松打造个性化在线工具箱

&#x1f389; Whats this&#xff1f; 这是一款在线工具箱程序&#xff0c;您可以通过安装扩展增强她的功能 通过插件模板的功能&#xff0c;您也可以把她当做网页导航来使用~ 觉得该项目不错的可以给个Star~ &#x1f63a; 演示地址 https://tool.aoaostar.com &#x1f…

MySQL 高级语句(一)

一、MySQL查询 1.1 排序 1.1.1 排序语法 1.1.2 order by案例 1.2 区间判断及查询不重复记录 1.2.1 区间判断 1.2.2 查询不重复记录 1.3 对结果进行分组 1.3.1 group by 语法 1.3.2 group by 案例 1.4 限制结果条目 1.4.1 LIMIT 语法 1.4.2 LIMIT 案例 1.5 设置别名…

分治——归并排序算法

例题一 解法&#xff08;归并排序&#xff09;&#xff1a; 算法思路&#xff1a; 归并排序的流程充分的体现了「分⽽治之」的思想&#xff0c;⼤体过程分为两步&#xff1a; ◦ 分&#xff1a;将数组⼀分为⼆为两部分&#xff0c;⼀直分解到数组的⻓度为 1 &#xff0c;使…

网络游戏推广利器:Xinstall一站式解决方案

随着网络游戏的飞速发展&#xff0c;游戏推广已成为游戏公司不可或缺的一环。然而&#xff0c;面对渠道繁多、效果评估困难、用户获取成本高昂等问题&#xff0c;许多游戏公司在推广过程中感到力不从心。今天&#xff0c;我们要为大家介绍一款强大的游戏推广利器——Xinstall。…

[leetcode] 100. 相同的树

给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a;true示例 2&a…

Linux 理解进程信号

目录 一、共享内存通信机制中的临界资源访问与同步控制 1、概念 2、生活角度理解信号机制 3、信号量的操作 二、信号 1、生活角度的信号 2、技术应用角度的信号 3、操作系统角度的信号 信号如何产生 理解组合键变为信号 理解信号如何被进程保存 时钟中断&#xff0…

基于《2023腾讯云容器和函数计算技术实践精选集》—探索腾讯云TKE的Docker容器、Serverless和微服务优势

重剑无锋&#xff0c;大巧不工。 ——金庸 腾讯云TKE&#xff0c;全称Tencent Kubernetes Engine&#xff0c;是一种完全托管式的容器服务。它可以帮助用户快速、高效地部署和管理Kubernetes集群&#xff0c;并提供一系列与之相关的云服务&#xff0c;如负载均衡、云硬盘、对象…

汇编语言第四版-第3章 寄存器(内访问)

al为ax的低字节&#xff0c;ax寄存器为累加器。

Vue element-plus 导航栏 [el-menu]

导航栏 [el-menu] Menu 菜单 | Element Plus el-menu有很多属性和子标签&#xff0c;为网站提供导航功能的菜单。 常用标签&#xff1a; 它里面有两个子标签。el-menu-item&#xff0c;它其实就是el-menu每一个里面的item&#xff0c;item就是真实匹配到路由的每个栏目&#…

Unity3d使用Jenkins自动化打包(Windows)(一)

文章目录 前言一、安装JDK二、安装Jenkins三、Jenkins插件安装和使用基础操作 实战一基础操作 实战二 四、离线安装总结 前言 本篇旨在介绍基础的安装和操作流程&#xff0c;只需完成一次即可。后面的篇章将深入探讨如何利用Jenkins为Unity项目进行打包。 一、安装JDK 1、进入…

机器学习优化算法(深度学习)

目录 预备知识 梯度 Hessian 矩阵&#xff08;海森矩阵&#xff0c;或者黑塞矩阵&#xff09; 拉格朗日中值定理 柯西中值定理 泰勒公式 黑塞矩阵&#xff08;Hessian矩阵&#xff09; Jacobi 矩阵 优化方法 梯度下降法&#xff08;Gradient Descent&#xff09; 随机…

Python问题列表

文章目录 1、使用pip安装的模块都存放到哪里了&#xff1f;2、安装fitz包报错&#xff0c;如何解决&#xff1f;3、python代码运行时&#xff0c;控制台输出乱码如何解决。4、vscode中第三方库不自动补齐 1、使用pip安装的模块都存放到哪里了&#xff1f; 答&#xff1a; pip是…

【OpenGL】使用 python + Qt + OpenGL 的现代渲染

伴随资源 目录 一、说明二、 关于PyQt6.x2.1 QOpenGLWidget详细说明2.2 绘画技巧 三、PyOpenGL四、OpenGL 管线五、Python集成开发环境5.1 Emacs配置5.2 pycharm环境 六、你好&#xff0c;OpenGL&#xff01;七、QGL控件八、平截头体.svg九、定义几何9.1 立即模式与保留模式9…

如何在Portainer中创建Nginx服务并搭建静态站点实现公网访问本地网站

文章目录 前言1. 安装Portainer1.1 访问Portainer Web界面 2. 使用Portainer创建Nginx容器3. 将Web静态站点实现公网访问4. 配置Web站点公网访问地址4.1公网访问Web站点 5. 固定Web静态站点公网地址6. 固定公网地址访问Web静态站点 前言 Portainer是一个开源的Docker轻量级可视…

ES学习日记(一)-------单节点安装启动

基于ES7.4.1编写,其实一开始用的最新的8.1,但是问题太多了!!!!不稳定,降到7.4 下载好的安装包上传到服务器或虚拟机,创建ES目录,命令mkdir -p /路径xxxx 复制安装包到指定路径并解压: tar zxvf elasticsearch-8.1.0-linux-x86_64.tar.gz -C /usr/local/es/ 进入bin目录安装,命…

JAVA学习笔记21(访问修饰符)

1.访问修饰符 ​ *基本介绍 ​ java提供四种访问控制修饰符号&#xff0c;用于控制方法和属性(成员变量)的访问权限(范围) 1.公开级别&#xff1a;用public修饰&#xff0c;对外公开 2.受保护级别&#xff1a;用protected修饰&#xff0c;对子类和同一个包中的类公开 3.默…

Linux基本指令篇

在前边&#xff0c;我们已经了解过了Linux操作系统的发展和应用&#xff0c;从该篇起&#xff0c;就正式进入对Linux的学习。 今天我们就来在Xshell上远程登录我们的云服务器。首先我们要知道自己云服务器的公网ip&#xff0c;然后修改一下密码。 点击跳转 修改完密码之后我们…

java题目15:从键盘输入n个数,求这n个数中的最大数与最小数并输出(MaxAndMin15)

每日小语 你是否有资格摆脱身上的枷锁呢&#xff1f;有许多人一旦获得解放&#xff0c;他的最后一点价值也就会跟着丧失。 ——尼采 自己敲写 它不按我想的来。。。 //从键盘输入n个数&#xff0c;求这n个数中的最大数与最小数并输出 import java.util.Scanner; public clas…

2024年美团笔试题(1)

一.题目描述 小美拿到了一个排列&#xff0c;其中初始所有元素都是红色&#xff0c;但有些元素被染成了白色。 小美每次操作可以选择交换任意两个红色元素的位置。她希望操作尽可能少的次数使得数组变成非降序&#xff0c;你能帮帮她吗? 排列是指:一个长度为n的数组&#…