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.mzph.cn/news/785599.shtml

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

相关文章

pytest教程-21-失败重跑插件-pytest-rerunfailures

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest插件的基本使用方法&#xff0c;本小节我们讲解一下pytest用例失败重跑插件pytest-rerunfailures。 测试环境不稳定偶发接口超时&#xff08;和服务无关&#xff0c;纯粹是环境问题&…

【C语言】带你完全理解指针(四)函数指针的应用sqort函数的实现

前言&#xff1a; 本文主要是函数指针的重要应用&#xff0c;介绍qsort函数以及模拟实现这样一个不限制使用数据类型的快速排序函数。 回调函数 函数指针有一个非常大的作用就是实现回调函数。非常重要 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针&#xf…

python如何画奥运五环

绘制奥运五环主要涉及到Python中的turtle绘图库运用&#xff1a; 程序源代码为&#xff1a; import turtle turtle.width(10) turtle.color(black) turtle.circle(50) turtle.penup() turtle.goto(120,0) turtle.pendown() turtle.color(red) turtle.circle(50) turtle.penup()…

基于springboot酒店管理平台

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于酒店管理平台系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了酒店管理平台系统&#xff0c;它彻底改变了过…

坚持刷题|分发饼干

文章目录 题目思路代码实现实现总结主要步骤时间复杂度 扩展问题 Hello&#xff0c;大家好&#xff0c;我是阿月。坚持刷题&#xff0c;老年痴呆追不上我&#xff0c;今天刷第一个贪心算法&#xff1a;分发饼干 题目 455.分发饼干 思路 要解决这个问题&#xff0c;可以使用…

Autodesk Maya 2025 mac玛雅三维动画特效软件

Autodesk Maya 2025 for Mac是一款功能强大、操作简便的三维动画软件&#xff0c;适用于电影、电视、游戏、建筑、工业设计、虚拟现实和动画等领域。无论是专业设计师还是初学者&#xff0c;都可以通过Maya 2025实现自己的创意和想法&#xff0c;创作出高质量的三维作品。 软件…

多尺度变换(Multidimensional Scaling ,MDS)详解

一、基本思想 MDS&#xff08;Multidimensional Scaling &#xff0c;MDS多维尺度变换&#xff09;是一种经典的降维算法&#xff0c;其基本思想是通过保持数据点之间的距离关系&#xff0c;将高维数据映射到低维空间中。 具体来说&#xff0c;MDS算法的基本步骤如下&#xff…

栈————顺序栈和链式栈

目录 栈 顺序栈 1、初始化顺序栈 2、判栈空 3、进栈 4、出栈 5、读栈顶元素 6、遍历 链式栈 1、初始化链式栈 2、断链式栈是否为空判 3、入栈(插入) ​编辑​编辑 4、出栈(删除) 5、读取栈顶元素 6、输出链式栈中各个节点的值&#xff08;遍历&#xff09; 栈 …

Express.js项目实战(1)—— 我的藏书馆

首先新建文件夹——myLibrary 在vscode中点击文件>点击 Duplicate Workspace(以工作区的方式打开文件夹myLibrary) 点击duplicate Workspace&#xff08;打开工作区&#xff09; 之后&#xff0c;会出现以下界面 点击打开文件夹&#xff0c;选择新建的文件夹&#xff0c;会出…

Vscode + PlatformIO + Arduino 搭建EPS32开发环境

Vscode PlatformIO Arduino 搭建EPS32开发环境 文章目录 Vscode PlatformIO Arduino 搭建EPS32开发环境1. Vscode插件安装2. 使用PlatformIO新建工程3.工程文件的基本结构4.一个基本的测试用例Reference 1. Vscode插件安装 如何下载vscode这里不再赘述&#xff0c;完成基本…

超低代码智能音频开发来了:XMOS宣布与DSP Concepts建立合作伙伴关系推动AIoT音频应用

人工智能和芯片供应商 XMOS 宣布与嵌入式音频软件专家 DSP Concepts 建立合作伙伴关系。该合作协议将允许音频开发人员将 XMOS 的高度确定性、低延迟的 xcore.ai 平台与 DSP Concepts 的 Audio Weaver 软件结合起来。该软件使用户能够利用多核算力以图形方式设计和调试音频和语…

EXCEL VBA与CSV的结合使用,提高多个CSV的处

EXCEL VBA与CSV的结合使用&#xff0c;提高多CSV的处理 1、首先创建一个文本文件&#xff0c;然后将copy *.csv 合并.csv粘贴&#xff0c;并重命名后缀未bat 2、将bat和多个CSV放一个路径下 3、双击运行合并.bat&#xff0c;运行完成会后生成合并.csv 4、将csv用Excel打开&…

ALPHA开发板上PHY网络芯片LAN8720

一. 简介 正点原子的开发板 ALPHA开发板&#xff0c;有线网络硬件方案所使用的也是最常用的一种方案&#xff0c;IMX6ULL芯片内部是自带 MAC网络芯片的&#xff0c;所以&#xff0c;也就是采用 "SOC内部集成网络MAC外设 PHY网络芯片方案"。 ALPHA开发板使用的PHY网…

算法第三十七天-旋转链表

旋转链表 题目要求 解题思路 题意&#xff1a;将链表中每个元素向后移动k个位置&#xff0c;相当于把链表的后面k%len节点移到链表的最前面。 所以本题步骤为&#xff1a; 1.求出链表长度&#xff1b; 2.找出倒数第k1个节点 3.链表重整&#xff1a;将连败哦的倒数第k1个节点和…

讲解pwngdb的用法,以csapp的bomb lab phase_1为例

参考资料 Guide to Faster, Less Frustrating Debugging 什么情况下会使用gbd 需要逆向ELF文件时(掌握gdb的使用&#xff0c;是二进制安全的基本功)开发程序时&#xff0c;程序执行结果不符合预期 动态调试ELF文件可以使用另外一种方法&#xff1a;IDA的远程linux动态调试。个…

java引入下载的依赖包配置

maven中央仓库没有依赖包&#xff0c;但是个别网站有可以下载的依赖包下载依赖包并引入的配置在maven工程目录下建立lib目录&#xff0c;并将下载的jar包放入在pom.xml文件中配置本地依赖 <dependency><groupId>org.ejml</groupId><artifactId>…

LeetCode——动态规划(Java)

动态规划 简介[简单] 509. 斐波那契数[简单] 70. 爬楼梯[简单] 746. 使用最小花费爬楼梯[中等] 62. 不同路径[中等] 63. 不同路径 II[中等] 343. 整数拆分[中等] 96. 不同的二叉搜索树背包问题01背包[中等] 416. 分割等和子集[中等] 1049. 最后一块石头的重量 II[中等] 494. 目…

【游戏分析】FPS游戏狩猎百发百中

某某游戏狩猎玩法及其类似于FPS游戏 即3D射击 所以同样拥有 自动瞄准功能和爆头功能 想达到百发百中我们就要精准的计算出3D朝向值 读取人物坐标 遍历怪物,读取怪物坐标比较简单,不过多陈诉 朝向自然而然一定是我们和敌人的坐标计算出来的 那么怎么计算的呢&#xff1f; 我…

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

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

基础知识系列

文章目录 一、linux passwd文件 一、linux passwd文件 linux中&#xff0c;用户名存放在/etc/passwd中&#xff0c;密码存放在/etc/shadow中。/etc/passwd只允许root用户可读可写&#xff0c;root组的用户可读&#xff0c;其他用户不可读不可写不可执行。/etc/shadow只允许roo…