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

简介: 哈喽大家好,我是陈明勇,本文分享的内容是 Go 的复合数据类型(数组、Slice 切片、Map)。如果本文对你有帮助,不妨点个赞,如果你是 Go 语言初学者,不妨点个关注,一起成长一起进步,如果本文有错误的地方,欢迎指出!

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

哈喽大家好,我是陈明勇,本文分享的内容是 Go 的复合数据类型(数组、Slice 切片、Map)。如果本文对你有帮助,不妨点个赞,如果你是 Go 语言初学者,不妨点个关注,一起成长一起进步,如果本文有错误的地方,欢迎指出!

前言

上一篇文章 一文熟悉 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

1.jpg

  • 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 的底层原理进行详细介绍。

目录
相关文章
|
1月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
134 1
|
1月前
|
存储 Java Go
【Golang】(3)条件判断与循环?切片和数组的关系?映射表与Map?三组关系傻傻分不清?本文带你了解基本的复杂类型与执行判断语句
在Go中,条件控制语句总共有三种if、switch、select。循环只有for,不过for可以充当while使用。如果想要了解这些知识点,初学者进入文章中来感受吧!
103 1
|
4月前
|
存储 人工智能 安全
深入理解 go sync.Map - 基本原理
本文介绍了 Go 语言中 `map` 在并发使用时的常见问题及其解决方案,重点对比了 `sync.Mutex`、`sync.RWMutex` 和 `sync.Map` 的性能差异及适用场景。文章指出,普通 `map` 不支持并发读写,容易引发错误;而 `sync.Map` 通过原子操作和优化设计,在某些场景下能显著提升性能。同时详细讲解了 `sync.Map` 的基本用法及其适合的应用环境,如读多写少或不同 goroutine 操作不同键的场景。
188 1
|
11月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
254 7
|
5月前
|
Go
【LeetCode 热题100】DP 实战进阶:最长递增子序列、乘积最大子数组、分割等和子集(力扣300 / 152/ 416 )(Go语言版)
本文深入解析三道经典的动态规划问题:**最长递增子序列(LIS)**、**乘积最大子数组** 和 **分割等和子集**。 - **300. LIS** 通过 `dp[i]` 表示以第 `i` 个元素结尾的最长递增子序列长度,支持 O(n²) 动态规划与 O(n log n) 的二分优化。 - **152. 乘积最大子数组** 利用正负数特性,同时维护最大值与最小值的状态转移方程。 - **416. 分割等和子集** 转化为 0-1 背包问题,通过布尔型 DP 实现子集和判断。 总结对比了三题的状态定义与解法技巧,并延伸至相关变种问题,助你掌握动态规划的核心思想与灵活应用!
215 1
|
6月前
|
Go 索引
Go语言数组的定义与操作 - 《Go语言实战指南》
本文介绍了 Go 语言中的数组(Array)相关知识,包括定义、初始化方式(默认、显式、指定索引及自动推导长度)、访问与修改、遍历方法(for 循环和 for range)、值类型特性(复制行为)、多维数组支持以及其与切片的区别。数组是定长且同类型的集合,适合性能敏感场景,但实际开发中更常用动态的切片(slice)。
211 11
|
11月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
234 67
|
9月前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
10月前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
284 9
|
5月前
|
安全 Java 数据库连接
让我们讲解一下 Map 集合遍历的方式
我是小假 期待与你的下一次相遇 ~
206 43

热门文章

最新文章