Go 版本:1.21.0
前言
随着 Go 1.21.0
版本的发布,新增了两个实用的泛型工具库:maps
和 slices
,它们分别提供了处理映射(map
)和切片常见操作的函数,减少了我们重复造轮子的过程,提高开发效率。本文将会对 maps
工具库进行介绍。
准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。
Maps
maps
是一个泛型工具库,该库包含了对任何类型都支持的实用函数,函数简介如下表所示:
函数 | 函数签名 | 功能 |
---|---|---|
Clone | func Clone[M ~map[K]V, K comparable, V any](m M) M | 该函数返回 m 的一个副本,底层基于浅层克隆去实现,使用普通赋值的方式去设置新的键值对 |
Copy | func Copy[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2) | 复制 src 中的所有键值对到 dst 中,如果 dst 中包含 src 中的任意 key ,则该 key 对应的 value 将会被覆盖 |
DeleteFunc | func DeleteFunc[M ~map[K]V, K comparable, V any](m M, del func(K, V) bool) | 删除 m 中满足 del 返回为 true 的任何键值对 |
Equal | func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool | 判断两个 map 是否包含相同的键值对,内部使用 == 进行比较 |
EqualFunc | func 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
的副本,底层基于浅层克隆去实现,使用普通赋值的方式去设置新的键值对。
代码示例:
go
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)
}
执行结果:
css
m1: map[programmer-01:{陈明勇 深圳} programmer-02:{张三 广州}]
m2: map[programmer-01:{陈明勇 深圳} programmer-02:{张三 广州}]
上述例子中,首先创建一个 map
类型的变量 m1
,然后通过 maps.Clone()
函数进行克隆,得到 m2
,最后通过打印结果可知 m2
的值和 m1
的值一样。
从函数的功能描述中可知,Clone
函数的原理是浅层克隆,那么修改克隆后的 map
任意 key
的 value
将有可能影响原 map
的 value
。
我们来看下下面的例子:
go
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"])
}
执行结果
css
m1: {陈明勇 深圳}, {张三 广州}
m2: {陈明勇 深圳}, {张三 广州}
m2 被修改后,m1: {陈明勇 深圳}, {张三 海口}
m2 被修改后,m2: {陈明勇 深圳}, {张三 海口}
与前面的示例不同,这个例子中的一个关键区别在于 value
是指针类型。从执行结果可以明显看出,如果 m1
的 value
是指针类型,那么在对克隆后的 m2
中的任意 key
对应的 value
进行修改操作后,都会直接影响到 m1
。这是因为 m1
和 m2
共享了同一组指向相同 Programmer
结构体的指针,因此对一个指针的修改会在两个 map
中都可见。
Copy
Copy
函数接收两个 map
参数 dst
和 src
,该函数的功能是复制 src
中的所有键值对到 dst
中,如果 dst
中包含 src
中的任意 key
,则该 key
对应的 value
将会被覆盖。
代码示例:
go
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)
}
执行结果:
arduino
map[City:广州 Name:陈明勇 Phone:123456789]
在上述例子中,首先创建了两个 map
变量,分别为 m1
和 m2
,然后通过 maps.Copy
函数,将 m2
中的键值对复制到 m1
中,最后打印复制后的结果。
根据结果可知,由于 m1
和 m2
都包含 key → City
,因此在执行复制操作后, m1
中的 key → City
对应的 value
值会被覆盖。
DeleteFunc
DeleteFunc
函数接收一个 map
类型的参数 m
和一个函数类型的参数 del
。该函数的功能是删除 m
中满足 del
返回为 true
的任何键值对。
代码示例:
go
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)
}
执行结果:
arduino
map[1:陈明勇 3:李四]
在上述例子中,首先创建了一个 map
变量 m1
,使用 int
类型作为学号(key
),string
类型作为姓名(value
),然后通过 maps.DeleteFunc
删除学号为双数的学生,匿名函数的逻辑是 当学号为双数时,返回 true。
总体来说这个例子相对简单,读者可根据实际应用场景进行使用 DeleteFunc
函数。
Equal
Equal
函数接收两个 map
变量,函数的返回值为 bool
类型。该函数的功能是判断两个 map
是否包含相同的键值对,内部使用 ==
进行比较。注意:map
类型的 key
和 value
必须是 comparable
类型。
代码示例:
go
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
}
执行结果:
arduino
false
true
上述例子中,首先创建了三个 map
类型变量,分别是 m1
、m2
和 m3
,然后通过 maps.Equal()
函数,对 m1
和 m2
以及 m1
和 m3
进行等价比较。执行结果与预期一致,m1
和 m3
是相等的,m1
和 m2
不相等。
EqualFunc
EqualFunc
函数类似 Equal
函数,只不过是通过 eq
函数进行比较值,键仍使用 ==
进行比较。注意: value
可以为任意类型(any
)。
代码示例:
go
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
}
执行结果:
arduino
false
true
上述例子中,首先创建了三个 map
类型变量,分别是 m1
、m2
和 m3
。这些 map
使用 int
类型作为编号(key
),User
类型作为用户信息(value
)。
接着,使用 maps.EqualFunc()
函数,对 m1
和 m2
以及 m1
和 m3
进行等价比较,在这个函数中,我们自定义了比较函数 eq
,其逻辑是只要两个 User
结构体的 IdCard
相同,就认为它们是同一个人(相等)。执行结果与预期一致,m1
和 m3
是相等的,m1
和 m2
不相等。
小结
本文对 Go
工具库 maps
进行详细介绍,包括其提供的函数 Clone
、Copy
、DeleteFunc
、Equal
和 EqualFunc
,并强调了使用这些函数时需要注意的地方。
总的来说,通过使用这些函数,减少了我们重复造轮子的过程,提高开发效率。
你使用 maps
工具库了吗?感受如何,欢迎留言探讨。