go语言中的切片是使用非常频繁的一个数据结构,对于他的去重,我们可以有以下3种方式
1. 切片slice去重 利用map的key不能重复的特性+append函数 一次for循环搞定
这个模式时间复杂度最低,效率最高, 如果go版本大于1.21推荐使用这种方式的泛型参数版本
// 改进版的slice去重
func UniqueSliceInt64(ss []int64) []int64 {newSS := make([]int64, 0) // 返回的新切片m1 := make(map[int64]byte) //用来去重的临时mapfor _, v := range ss {if _, ok := m1[v]; !ok {m1[v] = 1newSS = append(newSS, v)}}return newSS
}
泛型参数版本,需要go版本大于1.21 否则不能使用泛型参数
// 切片去重升级版 泛型参数 利用map的key不能重复的特性+append函数 一次for循环搞定
func Unique[T cmp.Ordered](ss []T) []T {size := len(ss)if size == 0 {return []T{}}newSlices := make([]T, 0) //这里新建一个切片,大于为0, 因为我们不知道有几个非重复数据,后面都使用append来动态增加并扩容m1 := make(map[T]byte)for _, v := range ss {if _, ok := m1[v]; !ok { //如果数据不在map中,放入m1[v] = 1 // 保存到map中,用于下次判断newSlices = append(newSlices, v) // 将数据放入新的切片中}}return newSlices
}
2. 切片去重 利用map的key不能重复的特性 2次for循环
下面这个使用了泛型参数
//go版本大于1.21的情况,可以使用泛型参数// 切片去重 泛型参数 利用map的key不能重复的特性 2次for循环
func Unique1[T cmp.Ordered](ss []T) []T {size := len(ss)if size == 0 {return []T{}}// 这个地方利用了map数据的key不能重复的特性,将切片的值当做key放入map中,达到去重的目的m1 := make(map[T]byte)for i := 0; i < size; i++ {m1[ss[i]] = 1}// 创建一个切片,长度为去重后的数据长度newSS := make([]T, len(m1))idx := 0for key := range m1 { // 循环map, 将key放入到切片中newSS[idx] = keyidx++}return newSS
}
cmp.Ordered泛型参数约束接口参考
注意上面的泛型参数约束 cmp.Ordered 是一个专门用于泛型类型约束的接口定义, 需要go版本大于1.21才能使用
type Ordered interface {~int | ~int8 | ~int16 | ~int32 | ~int64 |~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |~float32 | ~float64 |~string
}
3. 切片去重 双层for循环模式
这种方式方式直观,但是时间复杂度较高!
// 切片去重 双层for循环模式
func Unique0(ss []int64) (newSS []int64) {newSS = make([]int64, 0)for i := 0; i < len(ss); i++ {repeat := falsefor j := i + 1; j < len(ss); j++ {if ss[i] == ss[j] {repeat = truebreak}}if !repeat {newSS = append(newSS, ss[i])}}return
}
总结
对于golang的切片去重,效率最高的方式为map+append函数的方式,因为他们的时间复杂度是最低的,对于go版本大于1.21的推荐使用泛型参数的切片去重,这样一个函数就可以解决所有类型的切片去重问题,高效精简!
怎么样,你觉得那种方式更好呢? 或者说你有更好的去重方式? 欢迎盘龙区留言讨论