一文了解 Go 的复合数据类型(数组、Slice 切片、Map)

news2024/9/22 21:21:47

一文了解 Go 的复合数据类型[数组、切片 Slice、Map]

  • 前言
  • 数组
    • 数组的创建方式
    • 数组的遍历
  • Slice 切片
    • 切片的创建方式
    • 切片的遍历
    • 向切片追加元素
  • Map
    • Map 的创建方式
    • Map 的基本操作
      • 插入和修改
      • 删除
      • 查找操作
      • 遍历操作
      • 删除操作
  • 小结

耐心和持久胜过激烈和狂热。

前言

上一篇文章一文熟悉 Go 的基础语法和基本数据类型
,讲解了 Go 的基础语法和基本数据类型,本篇文章将对 Go 的复合数据类型(数组、切片 Slice、Map)进行介绍。

数组

数组是由特定元素组成的固定长度的序列,元素可以是Go 的原生类型(如整形、字符串型和浮点型等)和自定义类型。一个数组可以包含零个或多个元素。通过数组的下标索引可以高效访问和修改每个元素的值,索引从 0 开始,到数组长度 - 1 结束。

数组的创建方式

  • 第一种
    import "fmt"
    
    func main() {
    	var arr [5]int
    	fmt.Printf("%d, %d, %d, %d, %d\n", arr[0], arr[1], arr[2], arr[3], arr[4]) //0, 0, 0, 0, 0
    	arr[0] = 1
    	fmt.Println(arr[0]) // 1
    }
    
    通过隐式的方式初始化一个长度为 5 的 int 类型数组,数组下标索引从 0 开始,上面输出的值为 0, 0, 0, 0, 0,如果初始化数组的时候,不带初始值,那么默认情况下,数组里的每个元素都会被初始化为对应数据类型的默认值,int 类型的默认值为 0。通过下标索引可以直接访问元素的值和修改元素的值。
  • 第二种
    import "fmt"
    
    func main() {
    	var arr [5]int = [5]int{1}
    	var arr2 = [5]int{1, 2, 3, 4, 5}
    	fmt.Println(arr)       // [1 0 0 0 0]
    	fmt.Println(arr2)      // [1 2 3 4 5]
    }
    
    显式初始化数组时,可以使用数组字面值语法初始化一个元素或多个元素。
  • 第三种
    import "fmt"
    
    func main() {
    	var arr = [...]int{1, 2, 3, 4}
    	fmt.Println(arr)        // [1 2 3 4]
    	fmt.Printf("%T\n", arr) // [4]int
    }
    
    初始化数组时,如果长度的位置出现 ... 而不是数字,则表示数组的长度是根据初始值元素的个数去计算的。
  • 第四种
    import "fmt"
    
    func main() {
    	var arr = [...]int{5: 5}
    	fmt.Println(arr) // [0 0 0 0 0 5]
    }
    
    初始化数组时,通过 index: value 的形式对某个位置的元素进行初始化,其他位置的元素为默认值。

数组的遍历

  • 普通 for 循环
    import "fmt"
    
    func main() {
    	var arr = [5]int{1, 2, 3, 4, 5}
    	for i := 0; i < len(arr); i++ {
    		fmt.Printf("索引:%d, 值:%d\n", i, arr[i])
    	}
    }
    
    输出结果:
    索引:0, 值:1
    索引:1, 值:2
    索引:2, 值:3
    索引:3, 值:4
    索引:4, 值:5
    
  • for-range 循环
    import "fmt"
    
    func main() {
    	var arr = [5]int{1, 2, 3, 4, 5}
    	for index, value := range arr {
    		fmt.Printf("索引:%d, 值:%d\n", index, value)
    	}
    }
    
    index 为数组的下标索引,value 为元素值。
    输出结果:
    索引:0, 值:1
    索引:1, 值:2
    索引:2, 值:3
    索引:3, 值:4
    索引:4, 值:5
    

Slice 切片

  • 切片和数组长得很像,但它们各有各的特点。由于数组的长度是固定的这个限制,在使用 Go 的过程中很少直接使用数组,而是使用切片 slice,它是一个动态的序列,程序运行时可以对它动态添加元素。
  • 切片的数据结构如下所示
    type slice struct {
    	array unsafe.Pointer
    	len   int
    	cap   int
    }
    
    我们可以看到,切片包含三个字段:
    • array: 指向底层数组的指针;
    • len: 切片的长度,即切片中当前元素的个数;
    • cap: 底层数组的长度,也是切片的最大容量,cap 的值永远大于等于 len 的值。

切片的创建方式

  • 声明切片
    import "fmt"
    
    func main() {
    	var arr []int
    	fmt.Printf("长度:%d\n", len(arr))
    	fmt.Printf("容量:%d\n", cap(arr))
    	fmt.Println(arr)
    }
    
    以上的创建方式只是声明切片,并未初始化,arr 的值为 nil
  • 声明切片并初始化
    import "fmt"
    
    func main() {
    	var arr = []int{1, 2, 3, 4, 5}
    	fmt.Printf("长度:%d\n", len(arr)) // 5
    	fmt.Printf("容量:%d\n", cap(arr)) // 5
    	fmt.Println(arr)                // [1 2 3 4 5]
    }
    
  • 通过 make 函数来创建切片
    import "fmt"
    
    func main() {
    	/*
    		第一个参数 -> type 切片的类型
    		第二个参数 -> len 切片的长度
    		第三个参数 -> cap 切片的容量
    	*/
    	arr := make([]int, 2, 5)
    	fmt.Printf("长度:%d\n", len(arr)) // 2
    	fmt.Printf("容量:%d\n", cap(arr)) // 5
    	/*
    		第一个参数 -> type 切片的类型
    		第二个参数 -> len & cap 切片的长度和容量
    	*/
    	arr2 := make([]int, 5)
    	fmt.Printf("长度:%d\n", len(arr2)) // 5
    	fmt.Printf("容量:%d\n", cap(arr2)) // 5
    }
    
    
    通过 make 函数创建切片时,使用 make([]int, 2, 5) 的形式,指定了切片的长度为 2,容量为 5;如果使用 make([]int, 5) 这种形式,不指定容量,那么容量就等于切片的长度。
  • 基于存在的数组创建切片
    import "fmt"
    
    func main() {
    	arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    	sl := arr[2:4]
    	fmt.Println(sl) // [3 4]
    }
    
    采用 array[low : high] 语法基于一个已存在的数组创建切片,这种方式被称为数组的切片化。直接修改 sl 的元素值会影响 arr 的元素值,因为 sl 的底层数组是指向 arr 的。

切片的遍历

  • 普通 for 循环
    import "fmt"
    
    func main() {
    	var arr = []int{1, 2, 3, 4, 5}
    	for i := 0; i < len(arr); i++ {
    		fmt.Printf("索引:%d, 值:%d\n", i, arr[i])
    	}
    }
    
    输出结果:
    索引:0, 值:1
    索引:1, 值:2
    索引:2, 值:3
    索引:3, 值:4
    索引:4, 值:5
    
  • for-range 循环
    import "fmt"
    
    func main() {
    	var arr = []int{1, 2, 3, 4, 5}
    	for index, value := range arr {
    		fmt.Printf("索引:%d, 值:%d\n", index, value)
    	}
    }
    
    index 为数组的下标索引,value 为元素值。
    输出结果:
    索引:0, 值:1
    索引:1, 值:2
    索引:2, 值:3
    索引:3, 值:4
    索引:4, 值:5
    

向切片追加元素

使用 append 函数可以想切片追加元素

import "fmt"

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

追加的元素被放置在切片的尾部

Map

在这里插入图片描述

  • Map 表示的是一组无序的键值对( key → value ),在 Go 中的形式为 map[key_type]value_type
  • keyvalue 可以是同一种类型 map[int]int,也可以不是同一种类型 map[string]int
  • map 中对 value 的类型没有限制,但是对 key 却有限制,想要作为 mapkey,必须满足以下条件:
    • key 的类型必须支持 ==!= 比较操作符
      例如 int 类型的 ab 两个变量,是支持 a == ba != b 操作的,而 Go 语言中 Slicemapfunction 复合类型,是不支持 T == TT != T操作的,只支持 T == nil 的判空操作。

Map 的创建方式

  • 错误的创建方式
    func main() {
    	var m map[string]string
    	m["name"] = "chenmingyong"
    }
    
    只声明而未初始化,直接使用 m 则会报错 mnil
  • 使用复合字面值初始化 map 类型变量
    import "fmt"
    
    func main() {
    	m := map[string]string{}
    	m["name"] = "chenmingyong"
    	fmt.Println(m["name"]) // chenmingyong
    }
    
  • 使用复合字面值显式初始化 map 类型变量
    import "fmt"
    
    func main() {
    	m := map[string]string{
    		"name": "chenmingyong",
    	}
    	fmt.Println(m["name"]) // chenmingyong
    }
    
  • 使用 make 创建 map 类型变量
    func main() {
    	m1 := make(map[string]string)    // 不指定容量,默认会给一个初始值
    	m2 := make(map[string]string, 5) // 指定容量为 5
    }
    
    如果不指定 map 的容量,默认会给一个初始值。

Map 的基本操作

插入和修改

func main() {
	m := make(map[string]string)
	// 新增键值对
	m["name"] = "chenmingyong"
	fmt.Println(m["name"]) // chenmingyong
	// 修改 value
	m["name"] = "cmy"
	fmt.Println(m["name"]) // cmy
}

通过 m[key] = value 的形式对 map 进行插入和修改操作。

删除

import "fmt"

func main() {
	m := make(map[string]string)
	// 新增键值对
	m["name"] = "chenmingyong"
	fmt.Println(m["name"]) // chenmingyong
	delete(m, "name")
	fmt.Println(m["name"]) // ""
}

通过 delete(map, key) 方法,对 map 里面的键值对进行删除。

查找操作

import "fmt"

func main() {
	m := make(map[string]string)
	m["name"] = "chenmingyong"
	value, ok := m["name"]
	fmt.Println(ok, value) // true chenmingyong
	value2, ok2 := m["age"]
	fmt.Println(ok2, value2) // false
}

使用 comma ok 惯用法对 map 进行键查找和键值读取操作,第一个变量接收 value 的值,第二个变量用于判断 key 是否存在,类型为 bool,若 key 不存在,value 的值为对应 key 类型的默认值。

遍历操作

import "fmt"

func main() {
	m := make(map[string]string)
	m["name"] = "chenmingyong"
	m["addr"] = "china"
	for key, value := range m {
		fmt.Println(key, value)
	}
}

通过 for-range 的方式遍历,map 也仅仅支持这种方式的遍历。

删除操作

  • 1、通过遍历,逐个删除
    import "fmt"
    
    func main() {
    	m := make(map[string]string)
    	m["name"] = "chenmingyong"
    	m["addr"] = "china"
    	for key, _ := range m {
    		delete(m, key)
    	}
    	fmt.Println(len(m)) // 0
    }
    
  • 2、将 map 变量指向一个新的 map,旧的 map 将会被 gc 回收
    func main() {
    	m := make(map[string]string)
    	m["name"] = "chenmingyong"
    	m["addr"] = "china"
    
    	m = make(map[string]string)
    	fmt.Println(len(m)) // 0
    }
    

小结

本文对数组、Slice 切片和 Map 的定义和相关操作进行了介绍,后续文章会对 Slice 切片和 Map 的底层原理进行详细介绍。

如果本文对你有帮助,欢迎点赞收藏加关注,如果本文有错误的地方,欢迎指出!

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

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

相关文章

CMake Cookbook by Eric

I. Basics 关键字&#xff1a;CMake中的构建指令 指令的书写是大小写无关的&#xff1b; II. Project&#xff1a;指定项目名称和语言类型 命令格式&#xff1a;project(<PROJECT-NAME> [<language-name>...]) Note 项目名称<PROJECT-NAME>不需要与项目根…

论文阅读【7】HHM隐马尔科夫模型

1.隐马尔科夫模型&#xff08;HMM&#xff09;的介绍 隐马尔科夫模型有两个序列&#xff0c;上面一层序列的值称之为影藏值(隐式变量)&#xff0c;下面一个序列中的值被称为观察值&#xff0c;想这个的序列模型被称为生成模型&#xff08;Generate model&#xff09;。z表示的是…

Linux - lsof显示 tcp,udp 的端口和进程

文章目录功能语法示例lsof -i 显示 tcp&#xff0c;udp 的端口和进程等相关查看服务器 80 端口的占用情况使用 -p 查看指定进程打开的文件更多命令功能 lsof&#xff08;list open files&#xff09;是一个列出当前系统打开文件的工具。 lsof 需要访问核心内存和各种文件&…

【区块链技术与应用】(八)

https://blog.csdn.net/lakersssss24/article/details/125762826?spm1001.2014.3001.5501 https://blog.csdn.net/lakersssss24/article/details/126434147 https://blog.csdn.net/lakersssss24/article/details/126671408?spm1001.2101.3001.6650.3&utm_mediumdistribut…

[附源码]java毕业设计医院仪器设备管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

cubeIDE开发, stm32的OLED点亮及字符显示设计(基于SPI通信)

一、SPI 通信技术 显示屏&#xff08;LCD、OLED&#xff09;接口一般有I2C、SPI、UART、RGB、LVDS、MIPI、EDP和DP等。一般3.5寸以下的小尺寸LCD屏&#xff0c;显示数据量比较少&#xff0c;普遍采用低速串口&#xff0c;如I2C、SPI、UART。SPI&#xff08;Serial Peripheral I…

通用后台管理系统前端界面Ⅹ——前端数据表格的删除、查找

删除操作 1、拿到id或者行数据对象 2、查看后端接口方法&#xff0c;写api方法&#xff0c;将操作连接上后端 后端请求操作成功&#xff0c;但是前端数据表格未更新&#xff0c;最简单的一种方法数据删除后要重新获取数据》 依旧显示成功&#xff0c;但是前端数据表格未变化&…

Bert and its family

Bert没有办法一次性读入特别长的文本的问题。自注意力机制非常消耗时间和空间。 概率值最大取argmax&#xff0c;对应的下标 整体全部更新&#xff0c;所有参数都更新&#xff0c;比固定住pre-trained要好很多。 不做预训练&#xff0c;loss下降比较慢&#xff0c;收敛比较慢&a…

BIM在工程中的20种典型功能

1、BIM模型维护 根据项目建设进度建立和维护BIM模型&#xff0c;实质是使用BIM平台汇总各项目团队所有的建筑工程信息&#xff0c;消除项目中的信息孤岛&#xff0c;并且将得到的信息结合三维模型进行整理和储存&#xff0c;以备项目全过程中项目各相关利益方随时共享。 由于…

Java 微信关注/取消关注事件

Java 微信关注/取消关注事件一、需求、思路二、文档、配置配置步骤1配置步骤2三、代码1、引入依赖包2、controller3、封装消息对象4、service、解密5、工具包一、需求、思路 需求&#xff1a;用户订阅/取消订阅公众号时接收消息并保存到数据库中以便后续功能的处理。 思路&…

【分类-SVDD】基于支持向量数据描述 (SVDD) 的多类分类算法附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

机器学习-回归模型相关重要知识点

目录01 线性回归的假设是什么&#xff1f;02 什么是残差&#xff0c;它如何用于评估回归模型&#xff1f;03 如何区分线性回归模型和非线性回归模型&#xff1f;04 什么是多重共线性&#xff0c;它如何影响模型性能&#xff1f;05 异常值如何影响线性回归模型的性能&#xff1f…

Springboot Security 前后端分离模式自由接口最小工作模型

但凡讲解Springboot Security的教程&#xff0c;都是根据其本身的定义&#xff0c;前后端整合在一起&#xff0c;登录采用form或者basic。我们现在的很多项目&#xff0c;前后端分离&#xff0c;form登录已经不适用了。很多程序的架构要求所有的接口都采用application/json方式…

RabbitMQ顺序性、可靠性、重复消费、消息堆积解决方案

RabbitMQ顺序性、可靠性&#xff08;消息丢失&#xff09;、重复消费、消息堆积解决方案 顺序性 RabbitMQ使用过程中&#xff0c;有些业务场景需要我们保证顺序消费&#xff0c;例如&#xff1a;业务上产生三条消息&#xff0c;分别是对数据的增加、修改、删除操作&#xff0…

【Java八股文总结】之Linux常用指令

文章目录Linux简介一、Linux目录结构二、Linux常用指令Linux简介 一、Linux目录结构 bin&#xff08;binaries&#xff09;&#xff1a;存放二进制可执行文件。 sbin&#xff08;super user binaries&#xff09;&#xff1a;存放二进制可执行文件&#xff0c;只有root才能访…

怎么把图片转换成表格?分享三个简单方法给你

你们是否在工作的时候会遇到这样的情况&#xff1a;收到同事发来的一张表格图片&#xff0c;需要你进行汇总登记&#xff0c;通常这种时候&#xff0c;你们都会怎么做呢&#xff1f;是根据图片的内容&#xff0c;手动输入制作成一份表格吗&#xff1f;虽然这样子可以进行表格的…

c++ 旅行商问题(动态规划)

目录一、旅行商问题简介旅行商问题问题概述问题由来二、基本思路三、实现1、状态压缩2、状态转移四、代码复杂度分析一、旅行商问题简介 旅行商问题 TSP&#xff0c;即旅行商问题&#xff0c;又称TSP问题&#xff08;Traveling Salesman Problem&#xff09;&#xff0c;是数学…

网络编程基础知识

文章目录1、网络概念2、协议3、网络分层4、网络传输流程5、端口号1、网络概念 先有计算机还是先有网络呢&#xff1f; 答案是先有计算机&#xff0c;为了数据研究和沟通的需求产生的网络&#xff0c;网络的产生是为了提升效率的。 那什么是网络呢&#xff1f; 网络指的是网络协…

实现一个自定义的vue脚手架

开发背景 博客很久没有更新了&#xff0c; 今天更新一个好玩的&#xff0c;等我将vue3的东西彻底搞明白我会更新一个vue3的系列&#xff0c;到时候会更新稍微勤一点&#xff0c;在使用vuecli的时候发现他的脚手架很有意思&#xff0c;用了几年了&#xff0c;但是一直没有好好研…

HTML CSS 网页设计作业「动漫小站」

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…