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

 更新时间:2023年08月15日 10:54:16   作者:陈明勇  
随着 Go 1.21.0 版本的发布,新增了两个实用的泛型工具库:maps 和 slices,下面小编就带大家一起学习一下 maps 工具库的相关知识吧

前言

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

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

Maps

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

函数函数签名功能
Clonefunc Clone[M ~map[K]V, K comparable, V any](m M) M该函数返回 m 的一个副本,底层基于浅层克隆去实现,使用普通赋值的方式去设置新的键值对
Copyfunc Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2)复制 src 中的所有键值对到 dst 中,如果 dst 中包含 src 中的任意 key,则该 key 对应的 value 将会被覆盖
DeleteFuncfunc DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool)删除 m 中满足 del 返回为 true 的任何键值对
Equalfunc Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool判断两个 map 是否包含相同的键值对,内部使用 == 进行比较
EqualFuncfunc EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) 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 工具库了吗?

推荐阅读

Go 1.21新内置函数min、max和clear的用法详解

到此这篇关于深入探索Go 1.21中的 maps工具库的文章就介绍到这了,更多相关Go 1.21 maps内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用 go 实现多线程下载器的方法

    使用 go 实现多线程下载器的方法

    本篇文章带领大家学习使用go实现一个简单的多线程下载器,给她家详细介绍了多线程下载原理及实例代码,感兴趣的朋友跟随小编一起看看吧
    2021-10-10
  • GO语言操作Elasticsearch示例分享

    GO语言操作Elasticsearch示例分享

    这篇文章主要介绍了GO语言操作Elasticsearch示例分享的相关资料,需要的朋友可以参考下
    2023-01-01
  • 如何在Go语言中高效使用Redis的Pipeline

    如何在Go语言中高效使用Redis的Pipeline

    在 Redis 中,Pipeline 就像一条流水线,它允许我们将多个命令一次性发送到服务器,下面我们就来看看如何在Go语言中高效使用Redis的Pipeline吧
    2024-11-11
  • Go语言fmt.Sprintf格式化输出的语法与实例

    Go语言fmt.Sprintf格式化输出的语法与实例

    Go 可以使用 fmt.Sprintf 来格式化字符串,下面这篇文章主要给大家介绍了关于Go语言fmt.Sprintf格式化输出的语法与实例,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Go框架自动化工具Beego使用详解

    Go框架自动化工具Beego使用详解

    这篇文章主要为大家介绍了Go框架自动化工具Beego使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • golang封装一个执行命令行的函数(return stderr/stdout/exitcode)示例代码

    golang封装一个执行命令行的函数(return stderr/stdout/exitcode)示例代码

    在 Go 语言中,您可以使用 os/exec 包来执行外部命令,不通过调用 shell,并且能够获得进程的退出码、标准输出和标准错误输出,下面给大家分享golang封装一个执行命令行的函数(return stderr/stdout/exitcode)的方法,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • Go 语言json.Unmarshal 遇到的小问题(推荐)

    Go 语言json.Unmarshal 遇到的小问题(推荐)

    这篇文章主要介绍了 Go 语言json.Unmarshal 遇到的小问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • 一文带你深入理解Golang中的泛型

    一文带你深入理解Golang中的泛型

    Go 在泛型方面一直被诟病,因为它在这方面相对比较落后。但是,在 Go 1.18 版本中,泛型已经被正式引入,成为了 Go 语言中一个重要的特性。本文将会详细介绍 Go 泛型的相关概念,语法和用法,希望能够帮助大家更好地理解和应用这一特性
    2023-05-05
  • Go中的Timer 和 Ticker详解

    Go中的Timer 和 Ticker详解

    在日常开发中,我们可能会遇到需要延迟执行或周期性地执行一些任务,这个时候就需要用到 Go 语言中的定时器,本文将会对这两种定时器类型进行介绍,感兴趣的朋友一起看看吧
    2024-07-07
  • go语言实现依赖注入的示例代码

    go语言实现依赖注入的示例代码

    依赖注入和控制反转恰恰相反,它是一种具体的编码技巧,我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递给类来使用,本文将给大家介绍go语言实现依赖注入,需要的朋友可以参考下
    2024-01-01

最新评论