map 是 go 最直接高效的去重方案;slices.contains 因线性扫描导致 o(n²) 复杂度,不适用于去重;map[string]struct{} 比 map[string]bool 更省内存;go 1.21+ 才支持泛型去重函数。

map 是 Go 里最直接、可读性好、性能可控的去重方案,但“语言学习”不是 Go 的内置机制——你真正需要的是理解类型约束、内存分配模式和标准库演进带来的实操差异。
为什么不能用 slices.Contains 做去重
它只判断存在性,每次调用都得遍历整个目标切片:slices.Contains(result, item) 在大 slice 上会退化成 O(n²)。有人硬套这个写法,10 万条数据可能卡住几秒。
- 它不维护状态,无法累积去重逻辑
- 没有哈希加速,纯线性扫描
- 和去重目标完全不匹配,属于误用 API
map[T]struct{} 比 map[T]bool 更省内存
结构体 struct{} 占 0 字节,而 bool 占 1 字节。对百万级字符串去重,内存差可能达百 KB 级别。
- 声明方式:
seen := make(map[string]struct{}) - 插入写法:
seen[item] = struct{}{} - 查存在只需
_, ok := seen[item],值本身无意义
泛型去重函数在 Go 1.21+ 才真正可用
旧版 Go(comparable 约束能覆盖绝大多数场景,但仍有隐含限制:
- 结构体字段不能含
[]int、map[string]int、func()—— 否则编译报错invalid operation: cannot compare - 指针类型(如
*MyStruct)默认可比较,但比较的是地址而非内容,容易误判 - 若需按内容去重不可比较类型,必须手动实现哈希键(比如
fmt.Sprintf("%v", s)),但有性能和稳定性风险
排序 + slices.Compact 不等于“保持原序”
这是最容易被忽略的点:它先破坏顺序再压缩,结果是升序排列后的唯一值,不是原始出现顺序。
- 正确用法:
slices.Sort(nums); result := slices.Compact(nums) - 适用前提:允许重排、元素支持排序(即实现了
constraints.Ordered)、且内存敏感 - 错误预期:以为
slices.Compact能单独去重 —— 它只合并相邻重复项,未排序时无效
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











