Golang数组与切片

文章目录

  • 数组
    • 数组介绍
    • 数组的定义方式
    • 访问与修改数组元素
    • 遍历数组元素
    • 数组指针
  • 切片
    • 切片介绍
    • 切片的定义方式
    • 访问与修改切片元素
    • 添加切片元素
    • 切片的拷贝
    • 遍历切片元素
    • string的切片

数组

数组介绍

数组介绍

  • 在Go中,数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。
  • 长度是数组类型的一部分,数组声明后长度不能动态变化,只有数组元素类型相同并且数组长度相同,才算同一种数组类型。
  • 数组创建后,如果没有对数组元素进行赋值,则保留对应元素类型的默认值(数值类型为0,string类型为"",bool类型为false,引用类型为nil)。

数组的示意图如下:

在这里插入图片描述

数组的定义方式

数组的定义方式

方式一: 指明数组长度和元素类型,数组元素采用对应的默认值。

var arr1 [5]int
fmt.Printf("arr1 = %v\n", arr1)      // arr1 = [0 0 0 0 0]
fmt.Printf("arr1 type = %T\n", arr1) // arr1 type = [5]int

方式二: 指明数组长度和元素类型,并初始化数组元素。

var arr2 = [5]int{1, 2, 3, 4, 5}
fmt.Printf("arr2 = %v\n", arr2)      // arr2 = [1 2 3 4 5]
fmt.Printf("arr2 type = %T\n", arr2) // arr2 type = [5]int

方式三: 指明数组长度并初始化数组元素,数组长度根据初始化值的个数计算。

var arr3 = [...]int{1, 2, 3, 4, 5}
fmt.Printf("arr3 = %v\n", arr3)      // arr3 = [1 2 3 4 5]
fmt.Printf("arr3 type = %T\n", arr3) // arr3 type = [5]int

方式四: 通过索引的方式,对数组中对应的元素进行初始化。

var arr4 = [...]int{1: 20, 4: 100, 0: -1, 2: 9}
fmt.Printf("arr4 = %v\n", arr4)      // arr4 = [-1 20 9 0 100]
fmt.Printf("arr4 type = %T\n", arr4) // arr4 type = [5]int

访问与修改数组元素

访问与修改数组元素

Go中通过数组名[下标]的方式对数组元素进行访问和修改。如下:

package main

import "fmt"

func main() {
	// 访问与修改数组元素
	var arr = [...]int{1, 2, 3, 4, 5}
	fmt.Printf("arr[3] = %d\n", arr[3]) // arr[3] = 4
	arr[3] += 10
	fmt.Printf("arr = %v\n", arr) // arr = [1 2 3 14 5]
}

说明一下:

  • 数组中元素的下标从0开始,访问数组元素时下标必须在指定范围内,否则会产生报错。

遍历数组元素

for循环遍历

通过for循环对数组元素的下标进行迭代,然后通过数组名[下标]的方式访问数组中的各个元素。如下:

package main

import "fmt"

func main() {
	// 遍历数组元素
	var arr = [...]int{1, 2, 3, 4, 5}
	for i := 0; i < len(arr); i++ {
		fmt.Printf("arr[%d] = %d\n", i, arr[i])
	}
}

说明一下:

  • len是Go中的内建函数,可以用于获取数组的长度。

for range遍历

通过for range循环也能完成对数组元素的遍历。如下:

package main

import "fmt"

func main() {
	// 遍历数组元素
	var arr = [...]int{1, 2, 3, 4, 5}
	for index, value := range arr {
		fmt.Printf("arr[%d] = %d\n", index, value)
	}
}

说明一下:

  • 在for range循环中遍历数组时,每次迭代会返回两个值,第一个是元素的索引,第二个是元素的值,当遍历结束后会自动退出for range循环。

数组指针

数组指针

数组属于值类型,采用值传递的方式进行传参,函数内部对数组的操作不会影响到原始数组,如果希望在函数中对数组的操作影响到原始数组,可以通过数组指针的方式进行传参。如下:

package main

import "fmt"

func ModifyArray(arr *[5]int) {
	for i := 0; i < len(arr); i++ {
		arr[i] += 10
	}
}

func main() {
	// 数组指针
	var arr = [...]int{1, 2, 3, 4, 5}
	fmt.Printf("arr = %v\n", arr) // arr = [1 2 3 4 5]
	ModifyArray(&arr)
	fmt.Printf("arr = %v\n", arr) // arr = [11 12 13 14 15]
}

说明一下:

  • 数组的地址通过&数组名的方式来获取,其与数组中第一个元素的地址相同。
  • len函数可以接收数组指针作为参数,Go会自动对指针进行解引用,并返回该数组的长度。
  • 在Go中,可以通过数组指针[下标]的方式访问数组元素,Go会自动对指针进行解引用并将其视为一个数组。

切片

切片介绍

切片介绍

  • 在Go中,切片(Slice)代表变长的序列或动态数组,序列中每个元素都有相同的类型。
  • 切片是数组的引用,主要由三部分构成:指针、长度和容量,指针指向底层引用的数组,长度代表切片中实际存储的元素个数,容量代表数组中可容纳的元素个数。

切片的示意图如下:

在这里插入图片描述

切片属于引用类型,当切片类型变量进行赋值或传参时,本质就是将切片中的指针、长度和容量三个字段的值进行拷贝,因此最终两个切片变量底层指向的是同一个数组,其中一个变量对数组修改会影响到另一个切片变量。如下:

在这里插入图片描述

切片的定义方式

方式一:引用现有数组

在定义切片时,可以通过数组名[起始下标:结束下标]的方式引用现有数组。如下:

package main

import "fmt"

func main() {
	// 方式一:引用现有数组
	var arr = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var slice = arr[3:6]
	fmt.Printf("slice = %v\n", slice)    // slice = [4 5 6]
	fmt.Printf("len = %d\n", len(slice)) // len = 3
	fmt.Printf("cap = %d\n", cap(slice)) // cap = 7
}

在上述代码中,切片与数组的关系示意图如下:

在这里插入图片描述

说明一下:

  • 切片定义后需要先让其引用到一个数组,数组名[起始下标:结束下标]表示引用数组[起始下标, 结束下标)范围内的元素,如果从数组的第一个元素开始引用,则起始下标可省略,如果引用到数组的最后一个元素,则结束下标可省略。
  • 通过引用现有数组创建的切片,切片的指针字段指向引用数组中起始下标位置的元素,长度字段为起始下标结束下标的长度,容量字段通常为起始下标到数组末尾的长度。
  • len和cap都是Go中的内建函数,len函数可以用于获取切片的长度,cap函数可以用于获取切片的容量。
  • 通过引用现有数组创建的切片,切片底层指向的就是被引用的数组,因此通过切片对数组进行的修改会影响到被引用的数组。
  • 创建切片时也可以引用现有的切片,其引用方式和底层原理与引用现有的数组类似。

方式二:make切片

在定义切片时,可以通过make创建指定长度和容量的切片。如下:

package main

import "fmt"

func main() {
	// 方式二:make切片
	var slice = make([]int, 5, 10)
	fmt.Printf("slice = %v\n", slice)    // slice = [0 0 0 0 0]
	fmt.Printf("len = %d\n", len(slice)) // len = 5
	fmt.Printf("cap = %d\n", cap(slice)) // cap = 10
}

在上述代码中,切片示意图如下:

在这里插入图片描述

说明一下:

  • make是Go中的内建函数,可以用于分配并初始化一个指定长度和容量的切片,其第一个参数表示切片的类型,第二个参数表示切片的长度,第三个参数表示切片的容量,第三个参数若省略则默认等于切片的长度。
  • 使用make函数创建切片时,Go运行时会在堆上分配一块连续的内存区域来存储切片的底层数组,该数组的长度和容量根据传递给make函数的参数确定。
  • 通过make的方式创建切片后,如果没有给切片中的元素赋值,则保留对应元素类型的默认值(数值类型为0,string类型为"", bool类型为false,引用类型为nil)。

方式三:指定具体数组

在定义切片时,也可以直接指定具体的数组。如下:

package main

import "fmt"

func main() {
	// 方式三:指定具体数组
	var slice = []int{1, 2, 3, 4, 5}
	fmt.Printf("slice = %v\n", slice)    // slice = [1 2 3 4 5]
	fmt.Printf("len = %d\n", len(slice)) // len = 5
	fmt.Printf("cap = %d\n", cap(slice)) // cap = 5
}

在上述代码中,切片示意图如下:

在这里插入图片描述

说明一下:

  • 通过指定具体数组创建切片时,Go运行时也会在堆上分配一块连续的内存区域来存储切片的底层数组,该数组的长度和容量与指定的数组的长度相同,并根据指定的数组来初始化底层数组的元素值。

访问与修改切片元素

访问与修改切片元素

Go中通过切片名[下标]的方式对切片元素进行访问和修改。如下:

package main

import "fmt"

func main() {
	// 访问与修改切片元素
	var arr = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var slice = arr[3:6]
	fmt.Printf("slice[2] = %d\n", slice[2]) // slice[2] = 6
	slice[2] += 10
	fmt.Printf("slice = %v\n", slice) // slice = [4 5 16]
	fmt.Printf("arr = %v\n", arr)     // arr = [1 2 3 4 5 16 7 8 9 10]
}

说明一下:

  • 切片中元素的下标从0开始,访问切片元素时下标必须在指定范围内,否则会产生报错。
  • 上述代码中通过引用现有数组创建了切片,因此对切片元素修改后被引用数组中对应的元素也被修改。

添加切片元素

添加切片元素

Go中通过append函数在切片末尾追加元素。如下:

package main

import "fmt"

func main() {
	// 添加切片元素
	var slice = []int{1, 2, 3, 4, 5}
	var slice2 = append(slice, 6, 7, 8)
	fmt.Printf("slice = %v\n", slice)   // slice = [1 2 3 4 5]
	fmt.Printf("slice2 = %v\n", slice2) // slice2 = [1 2 3 4 5 6 7 8]

	slice = append(slice, slice2...)
	fmt.Printf("slice = %v\n", slice) // slice = [1 2 3 4 5 1 2 3 4 5 6 7 8]
}

说明一下:

  • append是Go中的内建函数,用于在切片的末尾追加一个或多个元素,其第一个参数表示被追加的切片,第二个参数是可变参数类型,可以接收多个待追加的元素。
  • append函数在追加元素时,如果被追加的切片容量足够,则直接在切片后追加元素,否则会重新分配一个更大的空间来存储追加后的结果,并将追加后的切片返回。
  • 对于切片类型的变量来说,可以通过切片名...的方式将切片中的元素展开成独立的元素序列。

切片的拷贝

切片的拷贝

Go中通过copy函数完成切片的拷贝操作。如下:

package main

import "fmt"

func main() {
	// 切片的拷贝
	var slice = []int{1, 2, 3, 4, 5}
	var slice2 = make([]int, 5)
	fmt.Printf("slice = %v\n", slice)   // slice = [1 2 3 4 5]
	fmt.Printf("slice2 = %v\n", slice2) // slice2 = [0 0 0 0 0]

	copy(slice2, slice)
	fmt.Printf("slice = %v\n", slice)   // slice = [1 2 3 4 5]
	fmt.Printf("slice2 = %v\n", slice2) // slice2 = [1 2 3 4 5]

	for i := 0; i < len(slice2); i++ {
		slice2[i] += 10
	}
	fmt.Printf("slice = %v\n", slice)   // slice = [1 2 3 4 5]
	fmt.Printf("slice2 = %v\n", slice2) // slice2 = [11 12 13 14 15]
}

在上述代码中,两个切片的示意图如下:

在这里插入图片描述

说明一下:

  • copy是Go中的内建函数,用于将一个切片中的元素拷贝到另一个切片中,其第一个参数代表目标切片,第二个参数代表源切片。
  • copy函数在拷贝切片时,会将底层的数组也进行一份拷贝,因此拷贝后两个切片底层引用的是不同的数组,彼此之间不会相互影响。
  • copy函数在拷贝时,如果源切片与目标切片的长度不同,则会选择两者之间较小的值作为拷贝元素的数量。

遍历切片元素

for循环遍历

通过for循环对切片元素的下标进行迭代,然后通过切片名[下标]的方式访问切片中的各个元素。如下:

package main

import "fmt"

func main() {
	// 遍历切片元素
	var slice = []int{1, 2, 3, 4, 5}
	for i := 0; i < len(slice); i++ {
		fmt.Printf("slice[%d] = %d\n", i, slice[i])
	}
}

for range遍历

通过for range循环也能完成对切片元素的遍历。如下:

package main

import "fmt"

func main() {
	// 遍历切片元素
	var slice = []int{1, 2, 3, 4, 5}
	for index, value := range slice {
		fmt.Printf("slice[%d] = %d\n", index, value)
	}
}

说明一下:

  • 在for range循环中遍历切片时,每次迭代会返回两个值,第一个是元素的索引,第二个是元素的值,当遍历结束后会自动退出for range循环。

string的切片

string的切片

Go中的string是由单个字节连接起来的,其底层就是一个byte数组,因此string也可以进行切片处理。如下:

package main

import "fmt"

func main() {
	// string的切片
	var s string = "Hello World"
	var s2 = s[1:]
	fmt.Printf("s = %s\n", s)        // s = Hello World
	fmt.Printf("s2 = %s\n", s2)      // s2 = ello World
	fmt.Printf("s2 type = %T\n", s2) // s2 type = string
}

在上述代码中,两个string的示意图如下:

在这里插入图片描述

说明一下:

  • 由于string在Go中是不可变的,因此对string进行切片操作只是生成了一个新的string,表示原始string的子串,这个子串仍然是string类型,它们底层引用的是同一个字符串。

由于string的切片仍然是string类型,因此是不可变的,如果希望对字符串进行修改,需要先将其转换为[]byte[]rune类型。如下:

package main

import "fmt"

func main() {
	// string的切片
	var s string = "Hello World"
	var slice = []byte(s)
	slice[2] = 'x'
	fmt.Printf("s = %s\n", s)              // s = Hello World
	fmt.Printf("slice = %s\n", slice)      // slice = Hexlo World
	fmt.Printf("slice type = %T\n", slice) // slice type = []uint8
}

说明一下:

  • 由于string是不可变的,为了保持其不可变性,在将string转换为[]byte[]rune类型时,底层会创建一个新的byte切片或rune切片,其中包含字符串的副本,因此转换后的变量与原string底层引用的不是同一个字符串,彼此之间不会相互影响。

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

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

相关文章

[Algorithm][队列][宽搜BFS][N叉树的层序遍历][二叉树的锯齿形层序遍历][二叉树最大宽度][在每个树行中找最大值]详细讲解

目录 1.N 叉树的层序遍历1.题目链接2.算法思路详解3.代码实现 2.二叉树的锯齿形层序遍历1.题目链接2.算法原理详解3.代码实现 3.二叉树最大宽度1.题目链接2.算法原理详解3.代码实现 4.在每个树行中找最大值1.题目链接2.算法原理详解3.代码实现 1.N 叉树的层序遍历 1.题目链接 …

数据结构之链表深度讲解

小伙伴们&#xff0c;大家好呀&#xff0c;上次听我讲完顺序表想必收获不少吧&#xff0c;嘿嘿&#xff0c;这篇文章你也一样可以学到很多&#xff0c;系好安全带&#xff0c;咱们要发车了。 因为有了上一次顺序表的基础&#xff0c;所以这次我们直接进入正题&#xff0c;温馨…

Activiti7 开发快速入门【2024版】

记录开发最核心的部分&#xff0c;理论结合业务实操减少废话&#xff0c;从未接触工作流快速带入开发。假设你是后端的同学学过JAVA和流程图&#xff0c;则可以继续向后看&#xff0c;否则先把基础课程书准备好先翻翻。 为什么要工作流 比起直接使用状态字段&#xff0c;工作…

【 书生·浦语大模型实战营】作业(六):Lagent AgentLego 智能体应用搭建

【 书生浦语大模型实战营】作业&#xff08;六&#xff09;&#xff1a;Lagent & AgentLego 智能体应用搭建 &#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方…

【机器学习】集成方法---Boosting之AdaBoost

一、Boosting的介绍 1.1 集成学习的概念 1.1.1集成学习的定义 集成学习是一种通过组合多个学习器来完成学习任务的机器学习方法。它通过将多个单一模型&#xff08;也称为“基学习器”或“弱学习器”&#xff09;的输出结果进行集成&#xff0c;以获得比单一模型更好的泛化性…

上海计算机学会2021年1月月赛C++丙组T2康托表

题目背景 康托是一名数学家&#xff0c;他证明了一个重要的定理&#xff0c;需要使用一张表&#xff1a; 这个表的规律是&#xff1a; 从上到下&#xff1a;每一行的分子依次增大&#xff1b;从左到右&#xff1a;每一列的分母依次增大。 康托以一种不重复、不遗漏的方式&am…

【深耕 Python】Quantum Computing 量子计算机(1)图像绘制基础

一、绘制静止图像 使用matplotlib库绘制函数图像y sin(pi * x): import math import matplotlib.pyplot as pltx_min -2.0 x_max 2.0N 1000x1 [] y1 []for i in range(N 1):x x_min (x_max - x_min) * i / Ny math.sin(math.pi * x)x1.append(x)y1.append(y)plt.xl…

关于继承~

继承 动物有猫、狗&#xff0c; 猫又分为加菲猫、布偶猫......&#xff1b;狗又有哈士奇、德国牧羊犬...... 我们发现&#xff0c;下一类除了拥有上一类的共性之外&#xff0c;还拥有自己的特性。 于是我们可以利用继承的方式来减少重复的代码 继承的基本语法 class A:p…

二叉树的直径

题目描述&#xff1a;给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root […

在剪映专业版中新增字体的方法

我一开始以为剪映专业版没有繁体字&#xff0c;结果发现有一个现代繁体&#xff0c;如图所示: 但是我已经下载了字体了&#xff0c;不用就可惜了。 点击汉仪粗黑繁&#xff0c;安装。 安装之后&#xff0c;重启电脑&#xff0c;打开剪映&#xff0c;就可以搜索到这个字体了。 这…

每日OJ题_贪心算法二④_力扣2418. 按身高排序

目录 力扣2418. 按身高排序 解析代码 力扣2418. 按身高排序 2418. 按身高排序 难度 简单 给你一个字符串数组 names &#xff0c;和一个由 互不相同 的正整数组成的数组 heights 。两个数组的长度均为 n 。 对于每个下标 i&#xff0c;names[i] 和 heights[i] 表示第 i 个…

罗宾斯《管理学》第13版/教材讲解/考研真题视频课程/网课

本课程是罗宾斯《管理学》&#xff08;第13版&#xff09;精讲班&#xff0c;为了帮助参加研究生招生考试指定考研参考书目为罗宾斯《管理学》&#xff08;第13版&#xff09;的考生复习专业课&#xff0c;我们根据教材和名校考研真题的命题规律精心讲解教材章节内容。 序号名…

读天才与算法:人脑与AI的数学思维笔记17_歌曲的创作公式

1. 人为何创作音乐 1.1. 音乐一直具有算法性质&#xff0c;这意味着在所有的艺术形式中&#xff0c;它受到人工智能进步的威胁最大 1.1.1. 音乐也是所有艺术形式中最抽象的一种&#xff0c;它利用结构和模式&#xff0c;而正是这种抽象的性质使它与数学紧密相连 1.1.2. 在这…

查找算法之二分查找

一、算法介绍 二分查找&#xff0c;也称为折半查找&#xff0c;是一种在有序数组中查找特定元素的高效算法。对于包含 n 个元素的有序数组&#xff0c;二分查找的步骤如下&#xff1a; 确定搜索范围&#xff1a;首先&#xff0c;将要查找的元素与数组中间的元素进行比较。如果…

【C++】学习笔记——string_5

文章目录 六、string类7. string类的模拟实现8. string类的模拟实现的完整代码string.h头文件test.c源文件 9. string收尾写时拷贝 未完待续 六、string类 7. string类的模拟实现 我们之前讲了实现 insert &#xff0c;但是那个插入函数仅仅是在 pos 位置插入一个字符而且&am…

13.1 QQ邮箱

1. 邮箱发送 2. 准备工作 3. 整合SpringBoot 3.1 配置 依赖引入 <!-- 邮件服务--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>application.…

发表博客之:transformer 架构 推理时候运算流程详细讲解,以及变长推理支持,小白都可以看得懂,AI推理工程师必备技能!

文章目录 [发表博客之&#xff1a;transformer 架构 推理时候运算流程详细讲解&#xff0c;以及变长推理支持&#xff0c;小白都可以看得懂&#xff0c;AI推理工程师必备技能&#xff01;](https://cyj666.blog.csdn.net/article/details/138439826)总结一下高性能变长推理 发表…

DevicData-P-XXXXXX勒索病毒有什么特点,如何恢复重要数据?

DevicData-P-XXXXXXXX这种新型勒索病毒有什么特点&#xff1f; DevicData-P-XXXXXXXX勒索病毒的特点主要包括以下几点&#xff1a; 文件加密&#xff1a;该病毒会搜索并加密用户计算机上的重要文件&#xff0c;如文档、图片、视频等&#xff0c;使其无法正常打开。这是通过强…

IoTDB 入门教程 问题篇②——RPC远程连接IoTDB服务器失败

文章目录 一、前文二、发现问题三、分析问题四、检查6667端口是否监听所有IP五、检查ECS云服务器的安全组是否允许六、检查Linux防火墙是否允许 一、前文 IoTDB入门教程——导读 二、发现问题 使用本地IP127.0.0.1可以连接IoTDB服务器使用远程IPxx.xx.xx.xx却连接不到。提示你…

抖音小店怎么运营操作,无货源正确做店方式,新手就看这一篇

大家好&#xff0c;我是电商笨笨熊 当前抖店还能做无货源模式吗&#xff1f; 当然可以。 近两年由于平台对于无货源的打击&#xff0c;很多人开始怀疑&#xff0c;无货源模式在抖店还能行得通吗&#xff1f; 抖店这个项目我们做了四年多的时间&#xff0c;无货源模式也是&a…
最新文章