巧用 Go Map 特性对数组或切片去重

时间:2022-12-12 11:18:14

耐心和持久胜过激烈和*。

哈喽大家好,我是陈明勇,今天分享的内容是巧用 Go Map 特性对数组或切片进行去重。如果本文对你有帮助,不妨点个赞,如果你是 Go 语言初学者,不妨点个关注,一起成长一起进步,如果本文有错误的地方,欢迎指出!

前言

在其他语言里(如 ​​Java​​​ )使用过 ​​Set​​​ 集合的小伙伴都知道,它的一个特点就是集合里的元素是不重复的。在一些需要去重场景中,我们可以使用 ​​Set​​​ 这种数据结构去存储数据,达到去重的目的。而在 ​​Go​​​ 语言里面,是没有 ​​Set​​ 这种数据类型的。如果我们想利用无重复元素的数据结构对数组或切片去重,可以使用 ​​Go​​​ 的复合数据类型 ​​Map​​​ 去构建 ​​Set​​​ 集合,对数组或切片进行去重。因为 ​​Map​​​ 中的 ​​key​​​ 是唯一的,如果不了解 ​​Map​​​ 的小伙伴,可以看看我的这篇文章 ​​一文了解 Go 的复合数据类型(数组、Slice 切片、Map) ​​。

使用 Map 构建 Set

type MySet map[any]struct{}

定义一个自定义类型 ​​MySet​​​ ,具有 ​​map[any]strcut{}​​ 的特性。

  • ​map​​​ 的 ​​key​​​ 是 ​​any​​​ 类型,任何类型都可以当做 ​​map​​​ 的 ​​key​​;
  • ​map​​​ 的 ​​value​​​ 是 ​​struct{}​​​ 类型,为什么会用结构体类型?是因为后面添加 ​​k-v​​​ 键值对时,指定的 ​​value​​​ 为 空结构体 ​​strcut{}{}​​​,​​fmt.Println(unsafe.Sizeof(struct{}{})) // 0​​ 空结构体是不占用内存空间的。

对数组或切片去重

import "fmt"

type MySet map[any]struct{}

func main() {
s1 := []int{1, 1, 2, 3}
fmt.Println(duplicateRemoving(s1)) // [1 2 3]
s2 := []string{"a", "b", "c", "a"}
fmt.Println(duplicateRemoving(s2)) // [a b c]
}

func duplicateRemoving[T any](s []T) []T {
res := make([]T, 0, len(s))
mySet := make(MySet)
for _, t := range s {
if _, ok := mySet[t]; !ok {
res = append(res, t)
mySet[t] = struct{}{}
}
}
return res
}

核心思路

  • 定义一个新的切片 ​​res​​​,为去重后的切片;定义一个 ​​MySet ​​​ 类型的变量 ​​mySet​​,用于判断元素是否重复;
  • 遍历原切片 ​​s​​​,首先判断 ​​mySet​​​ 里是否存在以切片 ​​s​​​ 的元素 ​​t​​​ 为 ​​key​​​ 的键值对,如果不存在,往新切片 ​​res​​​ 添加元素 ​​t​​​,往 ​​mySet​​​ 里添加键值对 ​​t → struct{}{}​​,否则说明元素重复,不做添加操作;
  • 返回去重后的新切片。

小结

本文介绍了如何利用 ​​Go​​​ 的复合数据类型 ​​Map​​​ 的特性对数组或切片进行去重。值得注意的一个地方是,在使用 ​​Map​​​ 构建 ​​Set​​​ 时,​​Value​​​ 的数据类型指定为 ​​struct{}​​​,原因是后面在添加键值对的时候,指定的 ​​Value​​​ 为空结构体 ​​strcut{}{}​​,空结构体不占用内存空间。