一文了解 Go 的复合数据类型(数组、Slice 切片、Map)
创始人
2024-04-12 06:44:06
0

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

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

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

前言

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

数组

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

数组的创建方式

  • 第一种
    import "fmt"func main() {var arr [5]intfmt.Printf("%d, %d, %d, %d, %d\n", arr[0], arr[1], arr[2], arr[3], arr[4]) //0, 0, 0, 0, 0arr[0] = 1fmt.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.Pointerlen   intcap   int
    }
    
    我们可以看到,切片包含三个字段:
    • array: 指向底层数组的指针;
    • len: 切片的长度,即切片中当前元素的个数;
    • cap: 底层数组的长度,也是切片的最大容量,cap 的值永远大于等于 len 的值。

切片的创建方式

  • 声明切片
    import "fmt"func main() {var arr []intfmt.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)) // 5fmt.Printf("容量:%d\n", cap(arr)) // 5fmt.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)) // 2fmt.Printf("容量:%d\n", cap(arr)) // 5/*第一个参数 -> type 切片的类型第二个参数 -> len & cap 切片的长度和容量*/arr2 := make([]int, 5)fmt.Printf("长度:%d\n", len(arr2)) // 5fmt.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]stringm["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// 修改 valuem["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"]) // chenmingyongdelete(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 chenmingyongvalue2, 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 的底层原理进行详细介绍。

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

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...