Go 专栏|复合数据类型:字典 map 和 结构体 struct |8 月更文挑战

简介: 本篇介绍复合数据类型的最后一篇:字典和结构体。内容很重要,编程时用的也多,需要熟练掌握才行。

QQ图片20220423131842.png


本篇介绍复合数据类型的最后一篇:字典和结构体。内容很重要,编程时用的也多,需要熟练掌握才行。


本文所有代码基于 go1.16.6 编写。


字典


字典是一种非常常用的数据结构,Go 中用关键词 map 表示,类型是 map[K]VKV 分别是字典的键和值的数据类型,其中键必须支持相等运算符,比如数字,字符串等。


创建字典


有两种方式可以创建字典,第一种是直接使用字面量创建;第二种使用内置函数 make

字面量方式创建:


// 字面量方式创建
var m = map[string]int{"a": 1, "b": 2}
fmt.Println(m) // map[a:1 b:2]
复制代码


使用 make 创建:


// 使用 make 创建
m1 := make(map[string]int)
fmt.Println(m1)
复制代码


还可以初始化字典的长度。在已知字典长度的情况下,直接指定长度可以提升程序的执行效率。


// 指定长度
m2 := make(map[string]int, 10)
fmt.Println(m2)
复制代码


字典的零值是 nil,对值是 nil 的字典赋值会报错。


// 零值是 nil
var m3 map[string]int
fmt.Println(m3 == nil, len(m3) == 0) // true true
// nil 赋值报错
// m3["a"] = 1
// fmt.Println(m3)  // panic: assignment to entry in nil map
复制代码

使用字典


赋值:


// 赋值
m["c"] = 3
m["d"] = 4
fmt.Println(m) // map[a:1 b:2 c:3 d:4]
复制代码


取值:


// 取值
fmt.Println(m["a"], m["d"]) // 1 4
fmt.Println(m["k"])         // 0
复制代码


即使在 Key 不存在的情况下,也是不报错的。而是返回对应类型的零值。


删除元素:


// 删除
delete(m, "c")
delete(m, "f") // key 不存在也不报错
fmt.Println(m) // map[a:1 b:2 d:4]
复制代码


获取长度:


// 获取长度
fmt.Println(len(m)) // 3
复制代码


判断键是否存在:


// 判断键是否存在
if value, ok := m["d"]; ok {
  fmt.Println(value) // 4
}
复制代码


和 Python 对比起来看,这个用起来就很爽。


遍历:


// 遍历
for k, v := range m {
  fmt.Println(k, v)
}
复制代码


引用类型


map 是引用类型,所以在函数间传递时,也不会制造一个映射的副本,这点和切片类似,都很高效。


package main
import "fmt"
func main() {
  ...
  // 传参
  modify(m)
  fmt.Println("main: ", m) // main:  map[a:1 b:2 d:4 e:10]
}
func modify(a map[string]int) {
  a["e"] = 10
  fmt.Println("modify: ", a) // modify:  map[a:1 b:2 d:4 e:10]
}
复制代码


结构体


结构体是一种聚合类型,包含零个或多个任意类型的命名变量,每个变量叫做结构体的成员。


创建结构体


首先使用 type 来自定义一个结构体类型 user,里面有两个成员变量,分别是:nameage


// 声明结构体
type user struct {
  name string
  age  int
}
复制代码


结构体的初始化有两种方式:


第一种是按照声明字段的顺序逐个赋值,这里需要注意,字段的顺序要严格一致。


// 初始化
u1 := user{"zhangsan", 18}
fmt.Println(u1) // {zhangsan 18}
复制代码

这样做的缺点很明显,如果字段顺便变了,那么凡是涉及到这个结构初始化的部分都要跟着变。


所以,更推荐使用第二种方式,按照字段名字来初始化。


// 更好的方式
// u := user{
//  age: 20,
// }
// fmt.Println(u) // { 20}
u := user{
  name: "zhangsan",
  age:  18,
}
fmt.Println(u) // {zhangsan 18}
复制代码


未初始化的字段会赋值相应类型的零值。


使用结构体


使用点号 . 来访问和赋值成员变量。


// 访问结构体成员
fmt.Println(u.name, u.age) // zhangsan 18
u.name = "lisi"
fmt.Println(u.name, u.age) // lisi 18
复制代码


如果结构体的成员变量是可比较的,那么结构体也是可比较的。


// 结构体比较
u2 := user{
  age:  18,
  name: "zhangsan",
}
fmt.Println(u1 == u)  // false
fmt.Println(u1 == u2) // true
复制代码


结构体嵌套


现在我们已经定义一个 user 结构体了,假设我们再定义两个结构体 adminleader,如下:


type admin struct {
  name    string
  age     int
  isAdmin bool
}
type leader struct {
  name     string
  age      int
  isLeader bool
}
复制代码


那么问题就来了,有两个字段 nameage 被重复定义了多次。


懒是程序员的必修课。有没有什么办法可以复用这两个字段呢?答案就是结构体嵌套。

使用嵌套方式优化后变成了这样:


type admin struct {
  u       user
  isAdmin bool
}
type leader struct {
  u        user
  isLeader bool
}
复制代码

代码看起来简洁了很多。


匿名成员


但这样依然不是很完美,每次访问嵌套结构体的成员变量时还是有点麻烦。


// 结构体嵌套
a := admin{
  u:       u,
  isAdmin: true,
}
fmt.Println(a) // {{lisi 18} true}
a.u.name = "wangwu"
fmt.Println(a.u.name)  // wangwu
fmt.Println(a.u.age)   // 18
fmt.Println(a.isAdmin) // true
复制代码


这个时候就需要匿名成员登场了,不指定名称,只指定类型。


type admin1 struct {
  user
  isAdmin bool
}
复制代码


通过这种方式可以省略掉中间变量,直接访问我们需要的成员变量。


// 匿名成员
a1 := admin1{
  user:    u,
  isAdmin: true,
}
a1.age = 20
a1.isAdmin = false
fmt.Println(a1)         // {{lisi 20} false}
fmt.Println(a1.name)    // lisi
fmt.Println(a1.age)     // 20
fmt.Println(a1.isAdmin) // false
复制代码


总结


本文介绍了字典和结构体,两种很常用的数据类型。虽然篇幅不长,但基本操作也都包括,写代码肯定是没有问题的。更底层的原理和更灵活的用法就需要大家自己去探索和发现了。


当然,我也会在写完基础专栏之后,分享一些更深层的文章,欢迎大家关注,交流。

到目前为止,数据类型就都介绍完了。


先是学习了基础数据类型,包括整型,浮点型,复数类型,布尔型和字符串型。然后是复合数据类型,包括数组,切片,字典和结构体。


这些都是 Go 的基础,一定要多多练习,熟练掌握。文中的代码我都已经上传到 Github 了,有需要的同学可以点击文末地址,自行下载。




文章中的脑图和源码都上传到了 GitHub,有需要的同学可自行下载。


地址:github.com/yongxinz/go…


目录
相关文章
|
11天前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
11天前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
2月前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
|
3月前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
131 71
|
3月前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
88 12
|
4月前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
61 2
|
10月前
|
编译器 Go C语言
go语言基本数据类型和变量
go语言基本数据类型和变量
61 0
|
Go Java
Golang数据类型和变量
数据类型 先来介绍一下Golang的数据类型。 布尔型 bool类型代表逻辑值,有真值true和假值false两种取值。 整数类型 整数类型有如下几种,这些整数都是有符号的类型,它们的无符号版本是类型名前面添加u,例如uint32。
779 0
|
Go 存储
02-Go语言数据类型与变量
Go基本类型 布尔型: bool - 长度: 1字节 - 取值范围: true,false - 注意事项: 不可以用数字代表true或false 整型: int/uint - 根据运行平台可能为32或64 8位整型:int8/uint8 - 长度: 1字节 - 取值范围: -128~127/0-25...
935 0
|
10天前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。