深入探索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语言将单反斜杠改为双反斜杠的方法

    最近开发的时候遇到这么个问题,就是在window上获取了文件目录的字段,然后将这个绝对路径保存到数据库,但是前端展示的时候路径的双反斜杠变成了单反斜杠,本文给大家介绍了使用go语言将单反斜杠改为双反斜杠的方法,需要的朋友可以参考下
    2024-01-01
  • GoFrame gredis缓存DoVar及Conn连接对象的自动序列化

    GoFrame gredis缓存DoVar及Conn连接对象的自动序列化

    这篇文章主要为大家介绍了GoFrame gredis干货DoVar Conn连接对象自动序列化详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • Go语言快速入门指针Map使用示例教程

    Go语言快速入门指针Map使用示例教程

    这篇文章主要为大家介绍了Go语言快速入门指针Map示例教程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Golang实现请求限流的几种办法(小结)

    Golang实现请求限流的几种办法(小结)

    这篇文章主要介绍了Golang实现请求限流的几种办法(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Go语言单控制器和多控制器使用详解

    Go语言单控制器和多控制器使用详解

    这篇文章主要为大家详细介绍了Go语言单控制器和多控制器的使用方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07
  • 简单聊一聊Go语言中的数组和切片

    简单聊一聊Go语言中的数组和切片

    数组和切片由于语法十分相似,在使用中容易混淆,要认真区分,下面这篇文章主要给大家介绍了关于Go语言中数组和切片的相关资料,需要的朋友可以参考下
    2021-07-07
  • Go语言动态并发控制sync.WaitGroup的灵活运用示例详解

    Go语言动态并发控制sync.WaitGroup的灵活运用示例详解

    本文将讲解 sync.WaitGroup 的使用方法、原理以及在实际项目中的应用场景,用清晰的代码示例和详细的注释,助力读者掌握并发编程中等待组的使用技巧
    2023-11-11
  • golang 占位符和fmt常见输出介绍

    golang 占位符和fmt常见输出介绍

    这篇文章主要介绍了golang 占位符和fmt常见输出介绍,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang 中反射的应用实例详解

    Golang 中反射的应用实例详解

    这篇文章主要为大家介绍了Golang 中反射的应用实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • golang下的GOPATH路径问题及解决

    golang下的GOPATH路径问题及解决

    为了方便,我一般使用task来管理项目的编译等事项,由于才入门go,所以碰到一个问题,以此篇为记,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01

最新评论