Go slices.Compact 怎样判断输入是否满足边界
来源:17golang原创
时间:2026-11-03 17:57:16 123浏览 收藏
如果你想判断一个输入是否适合交给 slices.Compact,先看“重复是否相邻”,再看结果长度是否变化。这个函数只会把连续相等的一段压成一个元素,不会排序,也不会把分散在切片不同位置的相同值自动合并。
官方文档:https://pkg.go.dev/slices#Compact
从 Go 1.21 开始,标准库提供了 slices 包。使用 Compact 前,可以把边界分成五类:nil 或空切片、只有一个元素、已经没有相邻重复、存在相邻重复、重复值不相邻。前四类的判断方式不同,最后一类则说明数据还不满足“连续压缩”的业务前提。
先确认输入是不是连续重复
Compact 的比较范围是相邻元素。下面的两组输入看起来都有重复值,但只有第一组会被压缩:
// 这段示例用相邻比较区分 Compact 能处理的重复和分散重复。
package main
import (
"fmt"
"slices"
)
func main() {
adjacent := []string{"go", "go", "rust", "rust", "go"}
separated := []string{"go", "rust", "go", "rust"}
adjacent = slices.Compact(adjacent)
separated = slices.Compact(separated)
fmt.Println(adjacent) // 结果只保留每段连续值的第一个元素。
fmt.Println(separated) // 分散重复没有相邻关系,仍会保留。
}
因此,标题里的“满足边界”不是判断切片里有没有重复值,而是判断重复是否以连续分组的形式出现。如果业务要求全局去重,需要先明确顺序、哈希表或排序策略,不能直接把 Compact 当成全局去重函数。

用相邻比较预判输入会不会变化
如果调用方需要在压缩前记录“输入是否已经满足边界”,可以只检查每一对相邻元素。泛型约束与 Compact 一样使用可比较元素:
// hasAdjacentEqual 只回答一个问题:切片里是否存在相邻相等元素。
// 它不排序、不修改输入,也不负责判断所有重复值。
func hasAdjacentEqual[S ~[]E, E comparable](s S) bool {
for i := 1; i
这个判断天然覆盖几个边界:
nil和[]T{}的长度都不超过 1,不存在相邻元素。- 单元素切片没有可比较的相邻位置,调用后长度不会变化。
- 每一对相邻值都不相等时,输入已经满足“没有连续重复”的边界。
- 只要找到一对相邻相等的值,结果就可能变短,但变短多少要由连续段长度决定。
要注意“可能变短”而不是“必然得到某个固定长度”。例如连续段可以出现在开头、中间或结尾,多个连续段也会同时被压缩。
调用后以返回长度作为结果边界
Compact 会返回处理后的切片,所以应当接住返回值。原来的切片变量不会因为函数在内部修改了底层数组,就自动拥有新的长度:
// 以返回值覆盖原变量,后续遍历才会使用压缩后的长度。
func compactNames(names []string) []string {
before := len(names)
names = slices.Compact(names)
after := len(names)
// before 和 after 用来记录业务上是否发生了压缩。
if after %d\n", before, after)
}
return names
}
如果只是想知道输入是否会变化,调用前的 hasAdjacentEqual 更直接;如果最终一定要得到压缩后的数据,则以 slices.Compact 返回值的长度为准。两者不要混成一个“检查函数”,否则容易把预判结果误当成处理结果。
别忽略 nil、空切片和元素类型
文档明确说明,Compact 会保留输入的 nil 性质。也就是说,nil 切片经过处理后仍然是 nil;非 nil 的空切片也不会凭空变成 nil。这一点在接口序列化、缓存命中和“未提供”与“提供但为空”的业务语义中很重要。
// 这个例子只展示 nil 性质的判断,不把 nil 和空切片混为一谈。
func sameNilness() {
var nilItems []int
emptyItems := make([]int, 0)
nilItems = slices.Compact(nilItems)
emptyItems = slices.Compact(emptyItems)
fmt.Println(nilItems == nil) // nil 输入仍保持 nil。
fmt.Println(emptyItems == nil) // 非 nil 空切片仍不是 nil。
}
此外,Compact 要求元素类型满足 comparable,因为它需要用相等运算判断相邻值。包含切片、映射或函数字段的复杂对象不能直接作为元素类型调用;这时应先定义业务等价关系,再考虑 slices.CompactFunc 是否适合。
理解底层数组复用带来的尾部边界
压缩通常会复用原切片的底层数组。返回值的 len 变短,cap 可能仍然保留;新长度之后的元素会被清零。调用方应该只通过返回切片访问有效数据,不要把原容量当成结果长度。

// 只把 returned 当作有效范围,避免误读底层数组尾部。
func compactTags(tags []string) []string {
tags = slices.Compact(tags)
// tags[:len(tags)] 才是业务有效数据;不要按 cap(tags) 遍历。
return tags
}
如果尾部元素曾经持有指针或较大的对象引用,清零可以帮助释放这些不再属于结果的引用。但这不代表压缩后就一定分配了新数组,也不代表原切片的所有别名都会同步缩短;多个切片视图仍可能指向同一个底层数组。
一张表判断是否满足使用边界
| 输入情况 | 是否属于 Compact 的处理范围 | 处理建议 |
|---|---|---|
| nil 或空切片 | 可以调用,结果长度不变 | 保留 nil 与空值语义 |
| 只有一个元素 | 可以调用,结果长度不变 | 无需为压缩额外分支 |
| 存在相邻相等元素 | 满足,结果可能变短 | 接住返回值并使用新长度 |
| 重复值不相邻 | 不满足全局去重目标 | 先重新组织数据或选择其他算法 |
| 元素不可比较 | 不能直接调用 Compact | 定义等价函数后评估 CompactFunc |
实际工程里,可以把判断顺序固定为:先确认目标是“压缩连续重复”,再检查元素是否可比较,最后接住返回值并只按新长度读取。这样既不会误删分散重复,也不会因为原切片容量还在就把尾部当成有效记录。
常见问题
slices.Compact 会自动排序吗?
不会。它只比较相邻元素,输入顺序不会被重新排列。
为什么调用后原切片看起来还有旧元素?
原变量的长度不会自动更新,或者你观察的是同一底层数组的其他切片视图。业务代码应使用函数返回值,并按新长度访问。
不相邻的重复值怎样删除?
先确定是否需要保序,再选择哈希集合、排序后压缩或其他专门算法;不要把 Compact 的相邻语义误当成全局去重。
什么时候考虑 CompactFunc?
当元素本身不可直接用 == 表达业务等价关系时,可以先设计比较函数,再确认连续相等的语义仍然符合需求。
-
860 收藏
-
843 收藏
-
826 收藏
-
809 收藏
-
792 收藏
-
382 收藏
-
202 收藏
-
413 收藏
-
134 收藏
-
152 收藏
-
476 收藏
-
243 收藏
-
501 收藏
-
396 收藏
-
501 收藏
-
292 收藏
-
483 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习