【启程Golang之旅】掌握Go语言数组基础概念与实际应用

欢迎来到Golang的世界!在当今快节奏的软件开发领域,选择一种高效、简洁的编程语言至关重要。而在这方面,Golang(又称Go)无疑是一个备受瞩目的选择。在本文中,带领您探索Golang的世界,一步步地了解这门语言的基础知识和实用技巧。

目录

初识数组

二维数组

slice切片

map映射


初识数组

        在Go语言中,数组是一种固定长度的、包含相同类型元素的序列。数组的长度是类型的一部分,因此[5]int和[10]int是两种不同的类型。数组在Go中是值类型,这意味着当你传递一个数组到函数或将其赋值给另一个变量时,实际上是在复制整个数组的内容。

在Go语言中数组的内存分配是静态的,并且是在编译时确定的。由于数组的长度是类型的一部分,所以不同长度的数组在内存中的表示和占用是不同的。以下是关于Go语言中数组内存分析的代码示例:

package main
import "fmt"
func main() {
	// 声明数组
	var arr [3]int16
	// 获取数组长度
	fmt.Println(len(arr)) // 3
	// 打印数组
	fmt.Println(arr) // [0 0 0]
	// 证明arr中存储的是地址值
	fmt.Printf("arr的地址为:%p \n", &arr) // arr的地址为:0xc0000100b0
	// 第一个空间地址
	fmt.Printf("arr[0]的地址为:%p \n", &arr[0]) // arr[0]的地址为:0xc0000100b0
	// 第二个空间地址
	fmt.Printf("arr[1]的地址为:%p \n", &arr[1]) // arr[1]的地址为:0xc0000100b2
	// 第三个空间地址
	fmt.Printf("arr[2]的地址为:%p \n", &arr[2]) // arr[2]的地址为:0xc0000100b4
}

因为int占两个字节,所以地址值之间间隔为2,如下是图例解释:

接下来我们通过用户终端输入的方式,将数据存入数组当中,然后遍历数组,将数据打印出来:

package main
import "fmt"
func main() {
	// 定义数组
	var scores [5]int
	// 将成绩存入数组
	for i := 0; i < len(scores); i++ {
		fmt.Printf("请录入第%d个学生的成绩", i+1)
		fmt.Scanln(&scores[i])
	}
	// 展示一下录入学生的成绩:(对数组进行遍历)
	for i := 0; i < len(scores); i++ {
		fmt.Printf("第%d个学生的成绩为:%d \n", i, scores[i])
	}
	// 求和
	sum := 0
	for i := 0; i < len(scores); i++ {
		sum += scores[i]
	}
	// 平均数
	avg := sum / len(scores)
	// 输出
	fmt.Printf("成绩的总和为:%v, 成绩的平均数为:%v", sum, avg)
}

最终呈现的效果如下所示:

当然这里也可以采用for range的方式,for range结构是go语言中特有的一种迭代结构,在许多情况下都非常有用,for range可以遍历数组、切片、字符串、map及通道,示例代码如下,最终也能得到上面图片的结果:

func main() {
	// 定义数组
	var scores [5]int
	// 将成绩存入数组
	for i := 0; i < len(scores); i++ {
		fmt.Printf("请录入第%d个学生的成绩", i+1)
		fmt.Scanln(&scores[i])
	}
	// 展示一下录入学生的成绩:(对数组进行遍历) for range方式
	for key, value := range scores {
		fmt.Printf("第%d个学生的成绩为:%d \n", key+1, value)
	}
}

数组可以有多种书写方式,如下可以看到我们数组的书写方式:

package main
import "fmt"
func main() {
	// 数组的几种书写方式
	// var scores [5]int = [5]int{94, 98, 89, 80, 90} // 基本写法
	// var scores = [5]int{94, 98, 89, 80, 90} // 省略类型和长度
	//var scores = [...]int{2: 66, 0: 33, 1: 99, 3: 88} // 确定数组下标对应的数值
	scores := []int{94, 98, 89, 80, 90} // 使用切片(slice)语法(这在Go中更为常见)
	sum := 0
	for i := 0; i < len(scores); i++ {
		// 求得成绩的总值
		sum += scores[i]
	}
	// 平均数
	avg := sum / len(scores)
	// 输出
	fmt.Printf("成绩的总和:%v, 成绩的平均数为:%v\n", sum, avg)
}

在go语言中,关于数组还有以下注意事项,这里做一个简单的概述:

长度属于类型的一部分:

func main() {
	// 定义一个数组
	var arr1 = [3]int{1, 2, 3}
	fmt.Printf("数组的类型为:%T", arr1) // [3]int

	var arr2 = [6]int{3, 4, 5, 6, 7, 8}
	fmt.Printf("数组的类型为:%T", arr2) // [6]int
}

数组属于值类型,在默认情况下是值传递,因此会进行值拷贝,如下图所示我对数组的值进行修改是修改了arr的值,并不是arr3的:

如想在其它函数中去修改原来的数组,可以使用引用传递(指针方式):

package main
import "fmt"
func test(arr *[3]int) {
	(*arr)[0] = 7
}

func main() {
	var arr3 = [3]int{3, 6, 9}
	test(&arr3)       // 传入arr3数组的地址
	fmt.Println(arr3) // [7 6 9]
}

二维数组

在Go语言中,二维数组(也称为矩阵)是一个数组,其中每个元素都是另一个数组。这意味着你可以有两个维度来索引数据:第一个维度表示行,第二个维度表示列。示例代码如下:

func main() {
	var arr1 [2][3]int = [2][3]int{{1, 2, 3}, {4, 5, 6}}
	fmt.Println(arr1) // [[1 2 3] [4 5 6]]
}

在下面的例子中,array2D 是一个5行3列的二维整型数组,这里可以通过array2D[row][column]的方式来访问或修改数组中的元素:

func main() {
	// 声明一个5行3列的二维整型数组
	var array2D [5][3]int
	// 初始化时直接赋值
	array2D = [5][3]int{
		{1, 2, 3},
		{4, 5, 6},
		{7, 8, 9},
		{10, 11, 12},
		{13, 14, 15},
	}
	// 访问二维数组中的元素
	fmt.Println(array2D[2][1]) // 输出: 8
}

如果想对二维数组进行遍历的话,需要用到双层for循环,这里进行一个简单的示例:

package main
import "fmt"
func main() {
	// 定义二维数组
	var arr = [3][3]int{{1, 4, 7}, {2, 5, 8}, {3, 6, 9}}
	fmt.Println(arr)
	fmt.Println("---------------")
	// 方式1:普通for循环
	for i := 0; i < len(arr); i++ {
		for j := 0; j < len(arr[i]); j++ {
			fmt.Print(arr[i][j], "\t")
		}
		fmt.Println()
	}
	// 方式2:for range循环
	for key, value := range arr {
		for k, v := range value {
			fmt.Printf("arr[%v][%v] = %v \t", key, k, v)
		}
		fmt.Println()
	}
}

最终达到的效果如下所示:

slice切片

在Go语言中,切片(slice)是对数组的一个连续片段的引用,它本身并不包含数组的数据,而是包含了对底层数组的引用、长度以及容量。切片提供了一种灵活、方便且高效的方式来操作数组的一个子集,是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷,如下代码给出切片的示例:

package main
import "fmt"
func main() {
	// 定义数组
	var intarr [6]int = [6]int{3, 6, 7, 1, 2, 3}
	// 定义切片 slice [1:3]表示切出的一段片段,索引从1开始,到3结束
	slice := intarr[1:3]
	// 输出数组
	fmt.Println("intarr: ", intarr) // intarr:  [3 6 7 1 2 3]
	// 输出切片
	fmt.Println("slice: ", slice) // slice:  [6 7]
	// 切片元素个数
	fmt.Println("slice的元素个数: ", len(slice)) // slice的元素个数:  2
	// 获取切片的容量,容量可以动态变化
	fmt.Println("slice的容量: ", cap(slice)) // slice的容量:  5
}

切片由3个字段的数据结构组成:指向底层数组的指针、切片的长度、切片的容量

切片的创建:在Go语言中,make 是一个内置函数,用于动态地分配并初始化对象,这些对象包括切片(slices)、映射(maps)和通道(channels)。特别是当我们谈论切片时,make 是创建它们的主要方式,使用 make 创建切片的基本语法如下所示:

slice := make([]T, length, capacity)

这里通过如下代码进行简单示例,在下面的示例中,我们创建了一个整数类型的切片 slice,其长度为 3,容量为 5。这意味着切片当前可以存储 3 个整数,但底层数组的大小为 5,因此将来可以在不重新分配内存的情况下向切片添加最多 2 个额外的整数。

func main() {
	// 定义切片:make函数的三个参数:1、切片类型 2、切片长度 3、切片容量
	slice := make([]int, 3, 5)
	fmt.Println(slice)               // [0 0 0]
	fmt.Println("切片的长度", len(slice)) // 切片的长度 3
	fmt.Println("切片的容量", cap(slice)) // 切片的容量 5
}

make底层创建一个数组,对外不可见,所以不可以直接操作这个数组,要通过slice去间接的访问各个元素,不可以直接对数组进行维护/操作。

切片的遍历:对切片的遍历可以采用如下的方式进行:

func main() {
	// 定义切片:make函数的三个参数:1、切片类型 2、切片长度 3、切片容量
	slice := make([]int, 4, 20)
	slice[0] = 66
	slice[1] = 77
	slice[2] = 88
	slice[3] = 99
	// 方式1:普通for循环,遍历切片
	for i := 0; i < len(slice); i++ {
		fmt.Printf("slice[%v] = %v \t", i, slice[i])
	}
	fmt.Println("\n------------------------")
	// 方式2:for range循环,遍历切片
	for index, value := range slice {
		fmt.Printf("slice[%v] = %v \t", index, value)
	}
}

最终呈现的效果如下所示:

切片的注意事项

1)切片定义后不可以直接使用,需要让其引用到一个数组,或者make一个空间供切片来使用

2)切片使用不能越界

3)切片的简写方式

var slice = arr[0:end] ----》 var slice = arr[:end]

var slice = arr[start:len(arr)] ----》 var slice = arr[start:]

var slice = arr[0:len(arr)] ----》 var slice = arr[:]

4)切片可以继续切片

5)切片可以动态增长

func main() {
	// 定义数组
	var intarr [6]int = [6]int{1, 4, 7, 3, 6, 9}
	// 定义切片
	var slice []int = intarr[1:4] // 4 7 3
	fmt.Println(len(slice))       // 3
	slice2 := append(slice, 88, 50)
	fmt.Println(slice2) // [4 7 3 88 50]
	fmt.Println(slice)  // [4 7 3]
	/* 底层原理
	   1.底层追加元素的时候对底层数组扩容,老数组扩容为新数组
	   2.创建一个新数组,将老数组中的 4,7,3 拷贝到新数组中,在新数组中追加 88,50
	   3.slice2底层数组的指向 指向的是新数组
	   4.往往我们在使用追加的时候其实想要做的效果是给slice追加元素,而不是给底层数组追加元素
	*/
	slice = append(slice, 10)
	fmt.Println(slice) // [4 7 3 10]
	// 底层的新数组不能直接维护,因为slice2底层数组的地址已经变了,所以我们需要重新创建一个新的切片,通过切片间接操作
	slice3 := []int{99, 44}
	slice = append(slice, slice3...)
	fmt.Println(slice) // [4 7 3 10 99 44]
}

6)切片的拷贝

func main() {
	// 定义数组
	var a []int = []int{1, 4, 7, 3, 6, 9}
	// 再定义一个切片
	var b []int = make([]int, 10)
	// 拷贝
	copy(b, a)     // 将a中的值拷贝到b中
	fmt.Println(b) // [1 4 7 3 6 9 0 0 0 0]
}

map映射

在Go语言中,map 是一种内置的数据结构,用于存储键值对(key-value pairs)的集合。map 提供了根据键来存储、检索和删除值的能力,使得在Go中处理关联数据变得非常方便,接下来通过如下代码示例进行演示:

func main() {
	// 定义map变量
	/*
		map: 这表示我们正在声明一个 map 类型的变量
		[int]string: 这部分定义了 map 的键和值的类型
		具体来说,int 是键的类型,而 string 是值的类型
		这意味着你可以将整数用作键,并将字符串作为与这些键相关联的值存储在 map 中
	*/
	var a map[int]string
	// 只声明map内存是没有分配空间的,需要使用make函数初始化分配空间
	a = make(map[int]string, 10) // map可以存放10个键值对,10可以不传,默认会分配一个起始大小
	// 初始化map
	a[3] = "one"
	a[1] = "two"
	a[2] = "three"
	// 输出集合
	fmt.Println(a) // map[1:one 2:two 3:three]
}

注意:map集合在使用前一定要make;map的key是按照从小到大排序;key值不能重复,如果重复后一个value会对前一个进行覆盖,value是可以重复的;make函数的第二个参数size可以省略,默认就分配一个内存

map的创建方式可以通过如下的操作进行:

func main() {
	// map的创建方式
	// 方式1
	a := make(map[int]string)
	a[2020] = "张三"
	a[2019] = "李四"
	fmt.Println(a) // map[2019:李四 2020:张三]
	// 方式2
	b := map[int]string{
		2019: "张三",
		2020: "李四",
	}
	b[2018] = "王五"
	fmt.Println(b) // map[2018:王五 2019:张三 2020:李四]
}

当然我们也可以通过map对数值进行增删改查的操作,具体如下:

增加和更新操作:map["key"]= value 一》如果key还没有,就是增加,如果key存在就是修改。

func main() {
	a := make(map[int]string)
	// 增加操作
	a[2020] = "张三"
	a[2019] = "李四"
	// 修改操作
	a[2020] = "张三!!!"
	fmt.Println(a) // map[2019:李四 2020:张三!!!]
}

删除操作:delete(map,“key"),delete是一个内置函数,如果key存在,就删除该key-value,如果k的y不存在,不操作,但是也不会报错。

func main() {
	a := make(map[int]string)
	// 增加操作
	a[2020] = "张三"
	a[2019] = "李四"
	// 删除操作
	delete(a, 2019)
	fmt.Println(a) // map[2020:张三]
}

清空操作:如果我们要删除map的所有key,没有一个专门的方法一次删除,可以遍历一下key,逐个删除;或者map=make(),make一个新的,让原来的成为垃圾,被gc回收。

查找操作:value ,bool = map[key];value为返回的value,bool为是否返回,要么true要么false。

func main() {
	a := make(map[int]string)
	// 增加操作
	a[2020] = "张三"
	a[2019] = "李四"
	// 查找操作
	value, flag := a[2019]
	if flag {
		fmt.Println(value) // 李四
	} else {
		fmt.Println("查找不到")
	}
	fmt.Println(a) // map[2020:张三]
}

当然map还有一些其他的操作,这里进行一个简单的演示:

package main
import "fmt"
func main() {
	a := make(map[int]string)
	a[2020] = "张三"
	a[2019] = "李四"
	a[2018] = "王五"
	// 获取长度
	fmt.Println(len(a))
	// 遍历
	for k, v := range a {
		fmt.Printf("key为:%v value为 %v \n", k, v)
	}
	// 上操作
	b := make(map[string]map[int]string) // 嵌套map
	// 赋值
	b["班级1"] = make(map[int]string)
	b["班级1"][2020] = "张三"
	b["班级1"][2019] = "李四"
	b["班级1"][2018] = "王五"
	b["班级2"] = make(map[int]string)
	b["班级2"][2019] = "张三1"
	b["班级2"][2018] = "李四1"
	b["班级2"][2017] = "王五1"
	// 遍历
	for k, v := range b {
		fmt.Println(k)
		for k1, v1 := range v {
			fmt.Printf("key为:%v value为 %v \n", k1, v1)
		}
	}
}

最终呈现的效果如下:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Gradle的学习

1.1 Gradle的优势 一款最新的&#xff0c;功能最强大的构建工具&#xff0c;用它逼格更高 使用Groovy或Kotlin代替XML&#xff0c;使用程序代替传统的XML配置&#xff0c;项目构建更灵活 丰富的第三方插件&#xff0c;让你随心所欲使用 完善Android,Java开发技术体系 1.2 …

深入解析数据库中的连接方法:四种关键技巧

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、连接方法的重要性 二、左连接&#xff08;Left Join&#xff09; 三、右连接&#xff…

C# yolov8 TensorRT Demo

C# yolov8 TensorRT Demo 目录 效果 说明 项目 代码 下载 效果 说明 环境 NVIDIA GeForce RTX 4060 Laptop GPU cuda12.1cudnn 8.8.1TensorRT-8.6.1.6 版本和我不一致的需要重新编译TensorRtExtern.dll&#xff0c;TensorRtExtern源码地址&#xff1a;https://githu…

【全开源】简单商城系统源码(PC/UniAPP)

提供PC版本、UniAPP版本(高级授权)、支持多规格商品、优惠券、积分兑换、快递鸟电子面单、支持移动端样式、统计报表等 提供全部前后台无加密源代码、数据库离线部署。 构建您的在线商店的基石 一、引言&#xff1a;为什么选择简单商城系统源码&#xff1f; 在数字化时代&am…

立创·天空星开发板-GD32F407VE-环境搭建

本文以 立创天空星开发板-GD32F407VET6-青春版 作为学习的板子&#xff0c;记录学习笔记。 立创天空星开发板-GD32F407VET6-环境搭建 单片机ARMARM内核系列Cortex-M系列常用ARM芯片厂商 GD32GD32的产品系列开发板开发板资源、尺寸标注图设计图纸 GD32F407 Keil ARM 安装下载地址…

浏览器下载的文件为什么不允许我指定下载位置

文章目录 问题分析 问题 我们在做B端开发时&#xff0c;通常会遇到用户问道&#xff1a;我下载的文件为什么不能下载到指定位置让我手动去选择呢 分析 我们开发是基于浏览器做的开发&#xff0c;因此要遵循浏览器的限制条件 浏览器限制用户自定义下载地址的主要原因是出于…

Autoware 技术代码解读(三)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&#xff0c;并附带200G…

Nacos 2.x 系列【12】配置加密插件

文章目录 1. 前言2. 安装插件2.1 编译2.2 客户端2.3 服务端 3. 测试 1. 前言 为保证用户敏感配置数据的安全&#xff0c;Nacos提供了配置加密的新特性。降低了用户使用的风险&#xff0c;也不需要再对配置进行单独的加密处理。 前提条件&#xff1a; 版本:老版本暂时不兼容&…

【最新区块链论文录用资讯】CCF A—INFOCOM 2024 共17篇

Conference&#xff1a;IEEE International Conference on Computer Communications CCF level&#xff1a;CCF A Categories&#xff1a;计算机网络 Year&#xff1a;2024 Num&#xff1a;17 A Generic Blockchain-based Steganography Framework with High Capacity via …

openssh升级

原因&#xff1a;因为低版本出现了漏洞 过程&#xff1a; 此时&#xff0c;root不能登录。 修改/etc/pam.d/login 第2行前面加上#&#xff0c;保存退出 /etc/pam.d/remote 第2行前面加上#&#xff0c;保存退出 此时root可以通过telnet登录了 二、升级openssl和openssh 【L…

校园周边美食探索及分享平台,基于 SpringBoot+Vue+MySQL 开发的前后端分离的校园周边美食探索及分享平台设计实现

目录 一. 前言 二. 功能模块 2.1. 前台首页功能模块 2.2. 用户功能模块 2.3. 管理员功能模块 三. 部分代码实现 四. 源码下载 一. 前言 美食一直是与人们日常生活息息相关的产业。传统的电话订餐或者到店消费已经不能适应市场发展的需求。随着网络的迅速崛起&#xff0…

深入分析 Android Activity (十一)

文章目录 深入分析 Android Activity (十一)1. Activity 的内存管理和优化1.1 内存泄漏的常见原因1.2 避免内存泄漏的方法1.3 内存泄漏检测工具 2. Activity 的配置变更处理2.1 处理配置变更2.2 保存和恢复状态2.3 使用 ViewModel 3. Activity 的测试3.1 单元测试3.2 UI 测试 4…

数据结构(三)栈 队列 数组

2024年5月26日一稿&#xff08;王道P78&#xff09; 栈 基本概念 基本操作 顺序存储结构 基本操作 共享栈 链式存储结构 队列 基本概念 顺序存储结构 循环队列 链式存储结构 基本操作 双端队列 栈和队列的应用 括号匹配 表达式求值 递归 层次遍历 计算机系统 数组和特殊矩阵…

绘唐app官方版绘唐3AI工具

绘唐app官方版绘唐3AI工具 激活授权方式&#xff1a;https://qvfbz6lhqnd.feishu.cn/wiki/CcaewIWnSiAFgokOwLycwi0Encf 绘唐app是一款基于人工智能和摄影技术的应用程序&#xff0c;旨在帮助用户将照片转化为唐朝画风的艺术作品。 该应用程序使用先进的图像处理算法&#xf…

上海亚商投顾:沪指冲高回落 电力、电网产业链持续爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日震荡调整&#xff0c;深成指、创业板指均跌超1%。电力、电网股再度爆发&#xff0c;众智科技、郴电国…

「西安邀请媒体参会」媒体宣发专访报道

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 一、媒体邀约目标 为提升活动的知名度和影响力&#xff0c;我们计划邀请西安地区的主流媒体、行业媒体以及网络媒体参与活动&#xff0c;并进行现场报道和专访。通过媒体的力量&#xff…

【云原生 | 59】Docker中通过docker-compose部署ELK

目录 1、组件介绍 2 、项目环境 2.1 各个环境版本 2.2 Docker-Compose变量配置 2.3 Docker-Compose服务配置 3、在Services中声明了四个服务 3.1 ElasticSearch服务 3.2 Logstash服务 3.3 Kibana服务 3.4 Filebeat服务 4、使用方法 4.1 方法一 4.2 方法二 5、启动…

解读makefile中的延迟变量与即时变量

在 Makefile 中&#xff0c;有两种类型的变量&#xff1a;即时变量&#xff08;immediate variable&#xff09;和延迟变量&#xff08;deferred variable&#xff09;。 它们在 Makefile 的执行过程中具有不同的特性和行为。 即时变量&#xff08;Immediate Variable&#x…

(C11) 泛型表达式

文章目录 ⭐语法⭐举例&#x1f6a9;判断对象类型&#x1f6a9;判断指针&#x1f6a9;函数重载&#x1f6a9;嵌套使用 END ⭐语法 Ref: 泛型选择 (C11 起) - cppreference.com 关键词&#xff1a; Genericdefault _Generic(控制表达式 , 关联列表) (C11 起) 关联列表 类型名:…

深度学习中文笔记.pdf

深度学习和机器学习应该如何入门呢&#xff1f;这是很多初学者经常提的问题&#xff0c;针对这个问题&#xff0c;相信很多过来人都会推荐吴恩达的在线课程。不过&#xff0c;由于是英文版本&#xff0c;就将很多人挡在了门外。 于是&#xff0c;在国内&#xff0c;以黄海广博士…