go 排序应优先使用 sort 包内置函数:基础类型用 sort.ints 等专用函数(要求非 nil 切片、正确类型);自定义类型统一用 sort.slice(需可寻址、字段导出、if-else 多条件);避免 sort.reverse 和手动实现排序。

Go 语言排序切片,别自己写快排或冒泡——sort 包已覆盖全部常用场景,关键在选对函数,否则轻则编译失败,重则 panic 或结果错乱。
基础类型升序直接用 sort.Ints / sort.Strings
整数、字符串、浮点数切片升序排序,就该无脑调用对应专用函数:
-
sort.Ints(nums)只接受[]int,传[]int64或[]interface{}会直接编译报错,这是好事——提前暴露类型误用 - 所有专用函数都是原地修改,不返回新切片;传
nil会 panic,调用前建议加if nums != nil - 含
nil指针的切片(如[]*string)会 panic,得先过滤:valid := make([]*string, 0, len(ptrs)); for _, p := range ptrs { if p != nil { valid = append(valid, p) } } - 数组要排序?必须先转切片:
var arr [5]int; sort.Ints(arr[:])——sort包所有函数只认切片
结构体或多字段排序统一走 sort.Slice
Go 1.8+ 后,95% 的自定义排序都该用 sort.Slice,闭包里写逻辑,不用定义新类型:
- 切片必须可寻址:不能写
sort.Slice([]Person{{}}, ...),会编译失败;得先赋值给变量再传 - 结构体字段必须导出(首字母大写),否则闭包里读不到值,比如
name string不行,Name string才行 - 多条件排序必须用
if-else分层,别堆&&:if a.Age != b.Age { return a.Age b.Name - 闭包中禁止捕获循环变量(如
for _, p := range people中的p),必须通过索引访问:people[i].Age - 时间比较别写
a.CreatedAt > b.CreatedAt,time.Time不支持直接比较运算符,改用a.CreatedAt.After(b.CreatedAt)
要降序或复杂规则时别硬套 sort.Reverse
sort.Reverse 只适合纯升序后翻转的简单场景,一遇到多字段、字符串长度、忽略大小写等需求就得换 sort.Slice:
-
sort.Reverse(sort.IntSlice(nums))只能翻转数值顺序;想按字符串长度降序?只能用sort.Slice(names, func(i, j int) bool { return len(names[i]) > len(names[j]) }) -
sort.Reverse返回的是一个实现了sort.Interface的包装器,必须再传给sort.Sort(),写法冗长且易错:sort.Sort(sort.Reverse(sort.StringSlice(words))) - 忽略大小写排序?
strings.ToLower(a) 写在 <code>sort.Slice闭包里就行,比绕一圈Reverse清晰得多 -
sort.Reverse对结构体无效——它只包装基础类型别名(如IntSlice),没提供PersonSlice这种东西
稳定性不是默认选项,需要时显式选 sort.SliceStable
普通 sort.Slice 底层是 pdqsort,不保证相等元素的原始顺序;只有当你依赖“同分数者按提交先后排列”这类逻辑时,才需切换:
-
sort.SliceStable接口和sort.Slice完全一致,只是算法换成归并排序,额外占用 O(n) 空间 - 常见误用:以为“按状态分组后再按时间排序”就一定需要 Stable——其实只要分组字段唯一,或时间字段本身参与主排序逻辑,
sort.Slice就够了 - 真正需要 Stable 的典型场景:日志聚合(同 level 日志保持到达顺序)、分页合并(多批查询结果中相同 ID 的记录需维持原始批次内顺序)
- 别为了“看起来更稳妥”而默认用 Stable——性能开销真实存在,且多数业务根本不需要这个语义
最常被忽略的一点:所有 sort 函数都不检查闭包中是否遗漏返回值。如果多条件判断漏掉 final return,程序可能 panic 或产生未定义行为——务必确保每个分支都有明确布尔返回。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











