深入探索 Go 1.21.0 中的 maps 工具库

简介: 本文对 Go 工具库 maps 进行详细介绍,包括其提供的函数 Clone、Copy、DeleteFunc、Equal 和 EqualFunc,并强调了使用这些函数时需要注意的地方。

Go 版本:1.21.0

前言

随着 Go 1.21.0 版本的发布,新增了两个实用的泛型工具库:mapsslices,它们分别提供了处理映射(map)和切片常见操作的函数,减少了我们重复造轮子的过程,提高开发效率。本文将会对 maps 工具库进行介绍。

准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。

Maps

maps 是一个泛型工具库,该库包含了对任何类型都支持的实用函数,函数简介如下表所示:

函数 函数签名 功能
Clone func CloneM ~map[K]V, K comparable, V any M 该函数返回 m 的一个副本,底层基于浅层克隆去实现,使用普通赋值的方式去设置新的键值对
Copy func CopyM1 ~map[K]V, M2 ~map[K]V, K comparable, V any 复制 src 中的所有键值对到 dst 中,如果 dst 中包含 src 中的任意 key,则该 key 对应的 value 将会被覆盖
DeleteFunc func DeleteFuncM ~map[K]V, K comparable, V any bool) 删除 m 中满足 del 返回为 true 的任何键值对
Equal func EqualM1, M2 ~map[K]V, K, V comparable bool 判断两个 map 是否包含相同的键值对,内部使用 == 进行比较
EqualFunc func EqualFuncM1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any bool) bool 类似 Equal 函数,但通过 eq 函数进行比较值,键仍使用 == 进行比较

Clone

Clone 函数接收一个 m 参数,该函数的功能是返回 m 的副本,底层基于浅层克隆去实现,使用普通赋值的方式去设置新的键值对。

代码示例:

package main

import (
    "fmt"
    "maps"
)

func main() {
   
    type Programmer struct {
   
        Name string
        City string
    }

    m1 := map[string]Programmer{
   
        "programmer-01": {
   Name: "陈明勇", City: "深圳"},
        "programmer-02": {
   Name: "张三", City: "广州"},
    }
    m2 := maps.Clone(m1)
    fmt.Printf("m1: %v\n", m1)
    fmt.Printf("m2: %v\n", m2)
}

执行结果:

m1: map[programmer-01:{陈明勇 深圳} programmer-02:{张三 广州}]
m2: map[programmer-01:{陈明勇 深圳} programmer-02:{张三 广州}]

上述例子中,首先创建一个 map 类型的变量 m1,然后通过 maps.Clone() 函数进行克隆,得到 m2,最后通过打印结果可知 m2 的值和 m1 的值一样。

从函数的功能描述中可知,Clone 函数的原理是浅层克隆,那么修改克隆后的 map 任意 keyvalue 将有可能影响原 mapvalue

我们来看下下面的例子:

package main

import (
    "fmt"
    "maps"
)

func main() {
   
    type Programmer struct {
   
        Name string
        City string
    }

    m1 := map[string]*Programmer{
   
        "programmer-01": {
   Name: "陈明勇", City: "深圳"},
        "programmer-02": {
   Name: "张三", City: "广州"},
    }
    fmt.Printf("m1: %v, %v\n", *m1["programmer-01"], *m1["programmer-02"])
    m2 := maps.Clone(m1)
    fmt.Printf("m2: %v, %v\n", *m2["programmer-01"], *m2["programmer-02"])
    m2["programmer-02"].City = "海口"
    fmt.Printf("m2 被修改后,m1: %v, %v\n", *m1["programmer-01"], *m1["programmer-02"])
    fmt.Printf("m2 被修改后,m2: %v, %v\n", *m2["programmer-01"], *m2["programmer-02"])
}

执行结果

m1: {陈明勇 深圳}, {张三 广州}
m2: {陈明勇 深圳}, {张三 广州}
m2 被修改后,m1: {陈明勇 深圳}, {张三 海口}
m2 被修改后,m2: {陈明勇 深圳}, {张三 海口}

与前面的示例不同,这个例子中的一个关键区别在于 value 是指针类型。从执行结果可以明显看出,如果 m1value 是指针类型,那么在对克隆后的 m2 中的任意 key 对应的 value 进行修改操作后,都会直接影响到 m1。这是因为 m1m2 共享了同一组指向相同 Programmer 结构体的指针,因此对一个指针的修改会在两个 map 中都可见。

Copy

Copy 函数接收两个 map 参数 dstsrc,该函数的功能是复制 src 中的所有键值对到 dst 中,如果 dst 中包含 src 中的任意 key,则该 key 对应的 value 将会被覆盖。

代码示例:

package main

import (
    "fmt"
    "maps"
)

func main() {
   
    m1 := map[string]string{
   "Name": "陈明勇", "City": "深圳"}
    m2 := map[string]string{
   "City": "广州", "Phone": "123456789"}
    maps.Copy(m1, m2)

    fmt.Println(m1)
}

执行结果:

map[City:广州 Name:陈明勇 Phone:123456789]

在上述例子中,首先创建了两个 map 变量,分别为 m1m2,然后通过 maps.Copy 函数,将 m2 中的键值对复制到 m1 中,最后打印复制后的结果。

根据结果可知,由于 m1m2 都包含 key → City,因此在执行复制操作后, m1 中的 key → City 对应的 value 值会被覆盖。

DeleteFunc

DeleteFunc 函数接收一个 map 类型的参数 m 和一个函数类型的参数 del。该函数的功能是删除 m 中满足 del 返回为 true 的任何键值对。

代码示例:

package main

import (
    "fmt"
    "maps"
)

func main() {
   
    m1 := map[int]string{
   1: "陈明勇", 2: "张三", 3: "李四", 4: "王五"}
    maps.DeleteFunc(m1, func(k int, v string) bool {
   
        return k%2 == 0
    })
    fmt.Println(m1)
}

执行结果:

map[1:陈明勇 3:李四]

在上述例子中,首先创建了一个 map 变量 m1,使用 int 类型作为学号(key),string 类型作为姓名(value),然后通过 maps.DeleteFunc 删除学号为双数的学生,匿名函数的逻辑是 当学号为双数时,返回 true

总体来说这个例子相对简单,读者可根据实际应用场景进行使用 DeleteFunc 函数。

Equal

Equal 函数接收两个 map 变量,函数的返回值为 bool 类型。该函数的功能是判断两个 map 是否包含相同的键值对,内部使用 == 进行比较。注意:map 类型的 keyvalue 必须是 comparable 类型。

代码示例:

package main

import (
    "fmt"
    "maps"
)

func main() {
   
    m1 := map[int]int{
   0: 0, 1: 1, 2: 2}
    m2 := map[int]int{
   0: 0, 1: 1}
    m3 := map[int]int{
   0: 0, 1: 1, 2: 2}

    fmt.Println(maps.Equal(m1, m2)) // false
    fmt.Println(maps.Equal(m1, m3)) // true
}

执行结果:

false
true

上述例子中,首先创建了三个 map 类型变量,分别是 m1m2m3,然后通过 maps.Equal() 函数,对 m1m2 以及 m1m3 进行等价比较。执行结果与预期一致,m1m3 是相等的,m1m2 不相等。

EqualFunc

EqualFunc 函数类似 Equal 函数,只不过是通过 eq 函数进行比较值,键仍使用 == 进行比较。注意: value 可以为任意类型(any)。

代码示例:

package main

import (
    "fmt"
    "maps"
)

func main() {
   
    type User struct {
   
        Nickname string
        IdCard   string
    }

    m1 := map[int]User{
   0: {
   Nickname: "陈明勇", IdCard: "111"}, 1: {
   Nickname: "张三", IdCard: "222"}}
    m2 := map[int]User{
   0: {
   Nickname: "陈明勇", IdCard: "111"}}
    m3 := map[int]User{
   0: {
   Nickname: "Go技术干货", IdCard: "111"}, 1: {
   Nickname: "张三", IdCard: "222"}}

    fmt.Println(maps.EqualFunc(m1, m2, func(user User, user2 User) bool {
   
        return user.IdCard == user2.IdCard
    })) // false
    fmt.Println(maps.EqualFunc(m1, m3, func(user User, user2 User) bool {
   
        return user.IdCard == user2.IdCard
    })) // true
}

执行结果:

false
true

上述例子中,首先创建了三个 map 类型变量,分别是 m1m2m3。这些 map 使用 int 类型作为编号(key),User 类型作为用户信息(value)。

接着,使用 maps.EqualFunc() 函数,对 m1m2 以及 m1m3 进行等价比较,在这个函数中,我们自定义了比较函数 eq,其逻辑是只要两个 User 结构体的 IdCard 相同,就认为它们是同一个人(相等)。执行结果与预期一致,m1m3 是相等的,m1m2 不相等。

小结

本文对 Go 工具库 maps 进行详细介绍,包括其提供的函数 CloneCopyDeleteFuncEqualEqualFunc,并强调了使用这些函数时需要注意的地方。

总的来说,通过使用这些函数,减少了我们重复造轮子的过程,提高开发效率。

你使用 maps 工具库了吗?感受如何,欢迎留言探讨。

目录
相关文章
|
4天前
|
网络协议 Linux Go
分享一个go开发的工具-SNMP Server
分享一个go开发的工具-SNMP Server
40 0
|
4天前
|
测试技术 API 开发者
【Docker项目实战】在Docker环境下部署go-file文件分享工具
【2月更文挑战第15天】在Docker环境下部署go-file文件分享工具
77 1
|
4天前
|
Go API 数据安全/隐私保护
Go语言标准库概览:构建高效、可靠的软件开发基石
【2月更文挑战第8天】Go语言标准库是Go语言生态系统的重要组成部分,它为开发者提供了一系列高效、可靠的工具和函数,帮助开发者构建高质量的软件应用。本文将对Go语言标准库进行概览,介绍其组成特点、设计哲学以及使用场景与优势,帮助读者更好地理解和应用Go语言标准库。
|
4天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
4天前
|
SQL 开发框架 .NET
你确定不学?Go标准库之 text/template
你确定不学?Go标准库之 text/template
10 2
|
4天前
|
运维 监控 Go
Golang深入浅出之-Go语言中的日志记录:log与logrus库
【4月更文挑战第27天】本文比较了Go语言中标准库`log`与第三方库`logrus`的日志功能。`log`简单但不支持日志级别配置和多样化格式,而`logrus`提供更丰富的功能,如日志级别控制、自定义格式和钩子。文章指出了使用`logrus`时可能遇到的问题,如全局logger滥用、日志级别设置不当和过度依赖字段,并给出了避免错误的建议,强调理解日志级别、合理利用结构化日志、模块化日志管理和定期审查日志配置的重要性。通过这些实践,开发者能提高应用监控和故障排查能力。
89 1
|
4天前
|
NoSQL Shell Go
在go中简单使用go-redis库
在go中简单使用go-redis库
|
4天前
|
中间件 Go API
Golang深入浅出之-Go语言标准库net/http:构建Web服务器
【4月更文挑战第25天】Go语言的`net/http`包是构建高性能Web服务器的核心,提供创建服务器和发起请求的功能。本文讨论了使用中的常见问题和解决方案,包括:使用第三方路由库改进路由设计、引入中间件处理通用逻辑、设置合适的超时和连接管理以防止资源泄露。通过基础服务器和中间件的代码示例,展示了如何有效运用`net/http`包。掌握这些最佳实践,有助于开发出高效、易维护的Web服务。
29 1
|
4天前
|
搜索推荐 Linux Go
分享一个go开发的端口转发工具-port-forward
分享一个go开发的端口转发工具-port-forward
32 0
|
4天前
|
Go
推荐一个go写的RTSP转直播工具
推荐一个go写的RTSP转直播工具
15 0