go切片去重需用map[t]bool或map[t]struct{}一次遍历实现保序高效,t必须可比较;不可比较类型需提取字段或定义辅助key;禁用slices.contains嵌套循环以防o(n²)。

Go 切片去重没有标准库函数,但用 map 一次遍历就能搞定,既保序又高效——别写双层循环,那是 O(n²) 的坑。
用 map[T]bool 做存在性检查,顺序天然保留
核心逻辑就一句:if !seen[v] { seen[v] = true; result = append(result, v) }。关键不是“先塞 map 再取 key”,而是边遍历边判断、边追加,seen 只起标记作用,不用于后续遍历。
- 必须用
map[T]bool或map[T]struct{},不能用map[T]int等带默认值的类型,否则_, ok := seen[v]判断会失效 -
map[T]struct{}更省内存(struct{}占 0 字节),但map[T]bool语义更直白,二者性能无差别 - 切片元素类型
T必须可比较(int、string、字段全可比较的 struct),否则编译报错:invalid map key type XXX - 示例:
seen := make(map[string]bool); result := make([]string, 0, len(src))—— 预分配容量能减少扩容次数
结构体切片去重前,先确认它支持 ==
结构体能直接用 == 判断相等,前提是所有字段类型都可比较。比如 type User { Name string; ID int } 可以;但含 map、slice、func 字段的结构体不行。
- 错误做法:对不可比较结构体硬套
map[User]bool→ 编译失败 - 替代方案:提取可比较字段(如
ID或Name)作为 map key,而不是整个结构体 - 千万别用
reflect.DeepEqual在循环里做判重——它慢、难调试、且掩盖了类型设计问题 - 如果必须按多字段组合去重,定义一个辅助 key 类型,例如
type key struct{ a, b string },确保它可比较
别踩 slices.Contains 的性能陷阱
Go 1.21+ 引入了 slices 包,但 slices.Contains(result, item) 是线性扫描,嵌套在主循环里会导致整体 O(n²) 复杂度,数据量一过千就明显变慢。
- 这是新手最常写的“简洁”代码,但实际是反模式
- 哪怕只处理几百个元素,也建议直接上
map方案,思维成本和性能收益都更优 -
slices.Contains适合一次性查找,不适合高频重复判重场景 - 如果真要泛型化,应封装成
func Unique[T comparable](s []T) []T,内部仍用 map 实现
真正容易被忽略的是类型约束——不是所有切片都能无脑套 map 去重,得先看编译器是否允许你把它当 map key。动手前花 10 秒检查下字段类型,比跑起来再 debug 要快得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











