【Go】Go数据类型详解—数组与切片

1. 前言

今天需要学习的是Go语言当中的数组与切片数据类型。很多编程语言当中都有数组这样的数据类型,Go当中的切片类型本质上也是对 数组的引用。但是在了解如何定义使用数组与切片之前,我们需要思考为什么要引入数组这样的数据结构。

1.1 为什么需要数组

❓ 现在有一个需求:对一个班级的学生做统一管理,能够方便的打印班级全部学生的姓名

我们通过以前的知识只能写出这样的一段代码:

var name1 = "zhangsan"
var name2 = "lisi"
var name3 = "wangwu"
fmt.Println(name1)
fmt.Println(name2)
fmt.Println(name3)

可想而知如果学生人数更多,代码量也会变得更为庞大,因此就出现了数组这样 连续存储结构 的数据类型,我们就可以定义一种数据类型就能对整个班级姓名做增删改查统一管理!

2. 数组的定义与使用

2.1 数组声明与访问

2.1.1 数组声明

数组的声明语法如下:

// var 数组名 [数组长度]元素类型
var stuNameArr [32]string

其中注意点如下:

  • 数组长度必须显示指定
  • 数组内部存储元素类型必须统一
  • 数组元素如果为基本类型且声明未赋值时默认为零值,比如string类型则为""、int为0
2.1.2 数组访问

数组访问则可以通过[] + 下标直接进行索引访问(下标从0开始)

// var 数组名 [数组长度]元素类型
var stuNameArr [32]string
stuNameArr[0] = "zhangsan"
stuNameArr[1] = "lisi"
fmt.Println(stuNameArr[0], stuNameArr[1])

同时数组也不能越界访问!!!在上述代码中可行索引区域为:[0, 32),因此使用stuNameArr[32]时就会出现编译错误,如果编译时检查不出越界则在运行过程中会出现panic错误信息!
在这里插入图片描述

2.1.3 数组声明并初始化

除了上述先声明后初始化的方式之外,Go中的数组还支持使用复合字面量的方式进行声明并初始化的过程,语法如下:

// var 数组名 = [数组长度]元素类型{元素值1, 元素值2...}
var nums = [3]int{1, 2, 3}
fmt.Println(nums, reflect.TypeOf(nums))

在这里插入图片描述

⭐ 扩展知识1:上述方式仍然需要我们手动指定数组长度,但是Go当中也提供了…的语法可以让编译器帮我们自动进行数组长度的计算!

// var 数组名 = [数组长度]元素类型{元素值1, 元素值2...}
var nums = [...]int{1,2,3, // 最后一个逗号不能省略
}
fmt.Println(nums, reflect.TypeOf(nums))

⭐ 扩展知识2:上述初始化方式只能从左到右依次赋值,不够灵活,Go还提供了更灵活的方式进行赋值——使用索引下标进行初始化赋值

// var 数组名 = [数组长度]元素类型{元素值1, 元素值2...}
var names = [3]string{0: "张三", 2: "李四"}
fmt.Println(names, reflect.TypeOf(names))
2.1.4 数组的迭代

迭代方式一:我们可以通过for循环的方式进行迭代,可以使用len内置函数得到数组的长度

var stus = [...]string{"zhangsan", "lisi"}
for i := 0; i < len(stus); i++ {fmt.Println(stus[i])
}

迭代方式二:Go语言还提供了range迭代收集器的方式进行遍历,其中i为元素下标,v为元素值,当然range关键字只适用于收集器

var stus = [...]string{"zhangsan", "lisi"}
for i, v := range stus {fmt.Println(i, v)
}

2. 切片

Go语言当中的切片是一种极其重要的数据类型,由于数组长度是固定的,因此操作起来十分麻烦,切片可以理解为是一个 动态数组,在开发中使用占比远远大于数组。

2.1 切片的创建方式

切片有如下两种创建方式:

  1. 创建方式1:使用数组的切片语法:arr[1:3]
  2. 创建方式2:使用make函数初始化:var slice = make([]int, len, cap)
2.1.1 创建方式1

首先先来看创建方式1:

var arr = [3]string{"zhangsan", "lisi", "wangwu"}
var slice = arr[1:3]
fmt.Println(arr, reflect.TypeOf(arr))
fmt.Println(slice, reflect.TypeOf(slice))

运行结果如下图所示:
在这里插入图片描述

❗ 注意:数组和切片虽然打印的形式非常类似,但是这是两种不同的数据类型,使用reflect.TypeOf数组的类型为 [3]string,但是切片的类型为[]string

切片语法:[startIndex:endIndex)

  1. 切片取到的区间为左闭右开
  2. 切片得到的元素数量为:endIndex - startIndex
  3. 数组和切片进行切片操作都能得到切片
  4. 当缺省开始位置时比如[:endIndex]返回从0位置到结束位置,当缺省结束位置时例如[startIndex:]表示从起始位置到区域末尾,两者都缺省例如[:]表示整个区间

练习题:

var arr = [5]int{10, 11, 12, 13, 14}
var s1 = arr[1:4]
fmt.Println(s1, reflect.TypeOf(s1)) 
var s2 = arr[2:5]
fmt.Println(s2, reflect.TypeOf(s2)) 
var s3 = s2[0:2]                    
s3[0] = 1000
fmt.Println(":::", s1, s2, s3)

运行结果如下图所示:
在这里插入图片描述

2.1.2 切片底层结构

翻看Go语言的源码就会发现,切片实际上就是一个结构体:内部结构如下:

// /src/runtime/slice.go
type slice struct {array unsafe.Pointerlen   intcap   int
}

各个字段含义如下:

  • array:该切片所引用的底层数组
  • len:切片的长度
  • cap:切片的容量,可用于扩容判断(后面会花大篇幅讲解)
var arr = [5]int{10, 11, 12, 13, 14}
var s1 = arr[1:4]
var s2 = arr[2:5]
var s3 = s2[0:2]                    

上述代码在内存中的结构是这样的:
在这里插入图片描述

  • arr指的就是底层数组所引用的起始位置地址
  • len指的就是当前切片的元素个数
  • cap指的是从引用的起始位置开始还剩余多少已分配空间可供使用
2.1.2.1 练习题1
var a = [...]int{1, 2, 3, 4, 5, 6}
a1 := a[0:3]
a2 := a[0:5]
a3 := a[1:5]
a4 := a[1:]
a5 := a[:]
a6 := a3[1:2]
fmt.Printf("a1的长度%d,容量%d\n", len(a1), cap(a1))
fmt.Printf("a2的长度%d,容量%d\n", len(a2), cap(a2))
fmt.Printf("a3的长度%d,容量%d\n", len(a3), cap(a3))
fmt.Printf("a4的长度%d,容量%d\n", len(a4), cap(a4))
fmt.Printf("a5的长度%d,容量%d\n", len(a5), cap(a5))
fmt.Printf("a6的长度%d,容量%d\n", len(a6), cap(a6))

运行结果:
在这里插入图片描述

2.1.2.2 练习题2
s1 := []int{1, 2, 3}
s2 := s1[1:]    
s2[1] = 4       
fmt.Println(s1) 

运行结果:

在这里插入图片描述

2.1.2.3 练习题3
var a = []int{1, 2, 3}
b := a
a[0] = 100
fmt.Println(b)

运行结果:

在这里插入图片描述

2.1.3 创建方式2(make函数)

跟指针类型类似,切片也是也是一种引用类型:因此声明未赋值时不会开辟空间进行初始化,如下代码是错误的:

var slice []int
slice[0] = 1

在指针章节我们通过new函数进行初始化并返回一个地址变量,但是在切片当中我们需要使用make函数进行初始化同时指定长度和容量参数

make函数基本语法:var slice = make(切片类型, 长度, 容量)

a := make([]int, 2) // 此时长度和容量都为2
b := make([]int, 2, 10)
fmt.Println(a, b) // [0, 0], [0, 0]
fmt.Println(len(a), len(b)) // 2 2
fmt.Println(cap(a), cap(b)) // 2 10

上述代码中我们使用make函数初始化a和b,第一行代码其内部构建了一个长度为2的数组,并初始化了一个切片数据类型,长度和容量都为2并指向对应底层数组;第二行代码其内部构建了一个长度为10的数组,并初始化了一个切片数据类型,长度为2容量为10并指向对应底层数组

2.2 切片扩容机制

append函数引入:由于数组长度是固定的,因此如果添加的元素过多就需要重新分配长度更大的数组并进行元素拷贝。而切片作为动态数组的优势就在于可以通过append函数自动进行扩容拷贝,简化了程序员开发成本

2.2.1 append基本用法

基本语法:append(切片, 元素值...)并返回一个新切片

var emps = make([]string, 3, 5)
emps[0] = "张三"
emps[1] = "李四"
emps[2] = "王五"
fmt.Println(emps) // ["张三", "李四", "王五"]
emps2 := append(emps, "rain")
fmt.Println(emps2) // ["张三", "李四", "王五", "rain"]
emps3 := append(emps2, "eric")
fmt.Println(emps3) // ["张三", "李四", "王五", "rain", "eric"]
// 容量不够时发生二倍扩容
emps4 := append(emps3, "yuan")
fmt.Println(emps4) // ["张三", "李四", "王五", "rain", "eric", "yuan"]

在这里插入图片描述

2.2.2 append扩容原理

💡 扩容机制:

  1. 如果当前切片的长度超过容量时,原数组空间不足以进行扩展,此时就会构建一个长度更大的新数组,并拷贝原数组的元素到新数组中,最后构建一个新的切片重新引用新数组并返回
  2. 当元素个数<1024的时候就进行二倍扩容,但是当元素个数>=1024时进行1.25倍扩容
2.2.2.1 练习题1
// 案例1
a := []int{11, 22, 33}
fmt.Println(len(a), cap(a)) // 3 3
c := append(a, 44)
a[0] = 100
fmt.Println(a) // [100, 22, 33]
fmt.Println(c // [11, 22, 33, 44]

在这里插入图片描述

运行结果:

在这里插入图片描述

2.2.2.2 练习题2
// 案例2
a := make([]int, 3, 10)
fmt.Println(a) // [0, 0, 0]
b := append(a, 11, 22) 
fmt.Println(a) // 小心a等于多少? [0, 0, 0]
fmt.Println(b) // [0, 0, 0, 11, 22]
a[0] = 100
fmt.Println(a) // [100, 0, 0]
fmt.Println(b) // [100, 0, 0, 11, 22]

在这里插入图片描述

运行结果:

在这里插入图片描述

2.2.2.3 练习题3
// 案例3
l := make([]int, 5, 10)
v1 := append(l, 1)
fmt.Println(v1) // [0, 0, 0, 0, 1]
fmt.Printf("%p\n", &v1)
v2 := append(l, 2) 
fmt.Println(v2) // [0, 0, 0, 0, 1, 2]
fmt.Printf("%p\n", &v2)
fmt.Println(v1) // [0, 0, 0, 0, 2]

运行结果:

在这里插入图片描述

2.2.2.4 经典面试题
arr := [4]int{10, 20, 30, 40}
s1 := arr[0:2] // [10, 20]
s2 := s1       //  // [10, 20]
s3 := append(append(append(s1, 1), 2), 3)
s1[0] = 1000
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(arr)

2.3 切片的其余操作

我们已经介绍完了切片当中的append核心操作,该操作用于新增元素,那么切片如果执行插入、删除等操作呢?

❗ 注意:切片类型并没有提供如delete、insert等操作,仅仅只有append一个操作,但是我们可以通过append模拟插入、删除等操作

2.3.1 使用append进行头插
var a = []int{1,2,3}
a = append([]int{0}, a...) // 在开头添加1个元素
a = append([]int{-3,-2,-1}, a...) // 在开头添加1个切片
2.3.2 使用append在任意位置插入
var a []int
a = append(a[:i], append([]int{x}, a[i:]...)...) // 在第i个位置插入x
a = append(a[:i], append([]int{1,2,3}, a[i:]...)...) // 在第i个位置插入切片
2.3.3 使用append进行删除
// 从切片中删除元素
a := []int{30, 31, 32, 33, 34, 35, 36, 37}
// 要删除索引为2的元素
a = append(a[:2], a[3:]...)
fmt.Println(a) //[30 31 33 34 35 36 37]

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

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

相关文章

flutter Get GetMiddleware 中间件不起作用问题

当使用 get: ^5.0.0-release-candidate-9.2.1最新版本时&#xff0c;中间件GetMiddleware各种教程都是让我们在redirect中实现&#xff0c;比如&#xff1a; overrideRouteSettings? redirect(String? route) {return RouteSettings(name: "/companyAuthIndexPage"…

【Idea启动项目报错NegativeArraySizeException】

项目场景&#xff1a; Idea启动项目报错&#xff08;打包不报错&#xff09;&#xff0c;项目在服务器部署运行没有问题&#xff0c;尝试了重启idea、重启电脑、maven clean/install 都不行 maven-resources-production:sample: java.lang.NegativeArraySizeException: -5833…

微信小程序:播放音频

在小程序开发中&#xff0c;音频播放是一个重要的功能。本文将详细介绍小程序音频播放的相关知识点&#xff0c;帮助开发者更好地掌握小程序音频播放的实现方法。 一、小程序音频播放的基本流程 在小程序中&#xff0c;音频播放的基本流程如下&#xff1a; 获取音频数据&#…

运行fastGPT 第四步 配置ONE API 添加模型

上次已经装好了所有的依赖和程序。 下面在网页中配置One API &#xff0c;这个是大模型的接口。配置好了之后&#xff0c;就可以配置fastGPT了。 打开 OneAPI 页面 添加模型 这里要添加具体的付费模型的API接口填进来。 可以通过ip:3001访问OneAPI后台&#xff0c;**默认账号…

RocketMQ 学习笔记01

一、MQ简介 1. 什么是MQ&#xff1f; MQ&#xff08;Message Queue&#xff0c;消息队列&#xff09; 是一种在分布式系统中用于实现进程间通信和数据传输的中间件。它通过在不同进程或应用程序之间传递消息&#xff0c;实现数据的异步处理、解耦和削峰填谷等功能。MQ广泛应用…

梁山派入门指南3——串口使用详解,包括串口发送数据、重定向、中断接收不定长数据、DMA+串口接收不定长数据,以及对应的bsp文件和使用示例

梁山派入门指南3——串口使用详解&#xff0c;包括串口发送数据、重定向、中断接收不定长数据、DMA串口接收不定长数据&#xff0c;以及对应的bsp文件和使用示例 1. 串口发送数据1.1 串口简介1.2 梁山派上的串口开发1.3 bsp_uart文件&#xff08;只发送不接收&#xff0c;兼容串…

Linux和Docker常用终端命令:保姆级图文详解

文章目录 前言1、Docker 常用命令1.1、镜像管理1.2、容器管理1.3、网络管理1.4、数据卷管理1.5、监控和性能管理 2、Linux 常用命令分类2.1、文件和目录管理2.2、用户管理2.3、系统监控和性能2.4、软件包管理2.5、网络管理 前言 亲爱的家人们&#xff0c;创作很不容易&#xf…

智能科技与共情能力加持,哈曼重新定义驾乘体验

2025年1月6日&#xff0c;拉斯维加斯&#xff0c;2025年国际消费电子展——想象一下&#xff0c;当您步入一辆汽车&#xff0c;它不仅能响应您的指令&#xff0c;更能理解您的需求、适应您的偏好&#xff0c;并为您创造一个独特且专属的交互环境。作为汽车科技领域的知名企业和…

关于2025年智能化招聘管理系统平台发展趋势

2025年&#xff0c;招聘管理领域正站在变革的十字路口&#xff0c;全新的技术浪潮与不断变化的职场生态相互碰撞&#xff0c;促使招聘管理系统成为重塑企业人才战略的关键力量。智能化招聘管理系统平台在这一背景下迅速崛起&#xff0c;其发展趋势不仅影响企业的招聘效率与质量…

机器视觉5-全连接神经网络

机器视觉5-全连接神经网络1 图像表示多层感知器全连接神经网络一、两层全连接网络表达式二、三层全连接网络表达式三、关于非线性操作的说明四、全连接神经网络的映射原理 全连接神经网络的权值一、线性分类器二、两层全连接网络三、总结 全连接神经网络线性不可分全连接神经网…

解锁转型密码:不同方向的技能与素质修炼手册

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 解锁…

ChatGPT提示词合集(国内大模型可参考使用)

行为迅速的Linux终端我想让你充当 linux 终端。我将输入命令&#xff0c;您将回复终端应显示的内容。我希望您只在一个唯一的代码块内回复终端输出&#xff0c;而不是其他任何内容。不要写解释。除非我指示您这样做&#xff0c;否则不要键入命令。当我需要用英语告诉你一些事情…

第三十八章 Spring之假如让你来写MVC——适配器篇

Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…

深度剖析RabbitMQ:从基础组件到管理页面详解

文章目录 一、简介二、Overview2.1 Overview->Totals2.2 Overview->Nodesbroker的属性2.3 Overview->Churn statistics2.4 Overview->Ports and contexts2.5 Overview->Export definitions2.6 Overview->Import definitions 三、Connections连接的属性 四、C…

使用 Python 编写一个简单的聊天机器人

&#x1f496; 欢迎来到我的博客&#xff01; 非常高兴能在这里与您相遇。在这里&#xff0c;您不仅能获得有趣的技术分享&#xff0c;还能感受到轻松愉快的氛围。无论您是编程新手&#xff0c;还是资深开发者&#xff0c;都能在这里找到属于您的知识宝藏&#xff0c;学习和成长…

Unity 自定义批量打包工具

打包配置项 using UnityEngine; using System.Collections.Generic;namespace MYTOOL.Build {[System.Flags]public enum VersionOptions{None 0,Major 1,Minor 4,Build 8,Revision 0x10,}/// <summary>/// 批量打包配置文件/// </summary>[CreateAssetMenu]…

JAVA实现五子棋小游戏(附源码)

文章目录 一、设计来源捡金币闯关小游戏讲解1.1 主界面1.2 黑棋胜利界面1.3 白棋胜利界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/145161039 JA…

Flink概述

一、Flink是什么 二、Flink特点 三、Flink vs SparkStreaming 表 Flink 和 Streaming对比 Flink Streaming 计算模型 流计算 微批处理 时间语义 事件时间、处理时间 处理时间 窗口 多、灵活 少、不灵活&#xff08;窗口必须是批次的整数倍&#xff09; 状态 有 …

基于SpringBoot的企业级工位管理系统【源码+文档+部署讲解】

系统介绍 基于SpringBootVue实现的企业级工位管理系统采用前后端分离架构方式&#xff0c;系统设计了管理员、员工两种角色&#xff0c;系统实现了用户登录与注册、个人中心、员工管理、部门信息管理、工位信息管理、使用情况管理、工位分配管理等功能。 技术选型 开发工具&…

Linux系统离线部署MySQL详细教程(带每步骤图文教程)

1、登录官网下载对应的安装包 MySQL :: Developer Zone 2、将压缩包上传到服务器上&#xff0c;这里直接上传到/usr/local路径上 使用sftp工具上传到/usr/local目录上 3、解压压缩包 tar -xf mysql-8.0.39-linux-glibc2.17-x86_64.tar.xz 4、将mysql-8.0.39-linux-glibc2.17…