Go 语言 sort 包详解:从基础排序到自定义排序(含底层原理+零基础看懂)
在 Go 开发中,排序是高频使用的基础功能,Go 标准库提供了开箱即用的 sort 包,无需依赖第三方库,就能完成基本类型切片排序 、自定义结构体排序 、逆序排序 、检查有序性等操作。它基于高效的排序算法实现,兼顾易用性与性能,是 Go 开发者必须掌握的核心工具。
本文将带你从零到一掌握 sort 包的所有常用用法,用最通俗的方式讲透底层原理,附可直接运行的代码示例。
一、sort 包核心特性
- 支持int、float64、string 三种基本类型的直接排序;
- 支持自定义类型/结构体 排序,只需实现
sort.Interface接口; - 支持逆序排序 、稳定排序(保持相等元素的原始顺序);
- 提供有序性检查 、二分查找等辅助函数;
- 底层使用优化后的快速排序/归并排序,性能优异。
二、基本类型切片排序(最常用)
sort 包为三种基本类型提供了直接调用的便捷函数,一行代码完成排序。
1. 整数切片排序
go
package main
import (
"fmt"
"sort"
)
func main() {
// 定义无序整数切片
nums := []int{9, 3, 6, 1, 7, 2}
// 升序排序(从小到大)
sort.Ints(nums)
fmt.Println("整数升序:", nums) // [1 2 3 6 7 9]
// 逆序排序(从大到小)
sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println("整数降序:", nums) // [9 7 6 3 2 1]
}
2. 浮点数切片排序
go
func main() {
floats := []float64{3.14, 1.59, 2.65, 0.78}
// 升序
sort.Float64s(floats)
fmt.Println("浮点数升序:", floats) // [0.78 1.59 2.65 3.14]
}
3. 字符串切片排序
字符串按照 Unicode 编码值 排序(数字 < 大写字母 < 小写字母)。
go
func main() {
strs := []string{"banana", "apple", "cherry", "123", "Dog"}
sort.Strings(strs)
fmt.Println("字符串排序:", strs) // [123 Dog apple banana cherry]
}
三、自定义结构体排序(核心用法)
实际开发中,我们经常需要对结构体切片按某个字段排序(比如按年龄、分数、价格排序)。
实现方式:两种方案
方案 1:实现 sort.Interface 接口(标准用法)
sort.Interface 要求实现 3 个方法:
Len() int:返回切片长度Less(i, j int) bool:排序规则(i 位置元素是否排在 j 前面)Swap(i, j int):交换两个元素
示例:对学生结构体按分数降序排序
go
package main
import (
"fmt"
"sort"
)
// 定义学生结构体
type Student struct {
Name string
Score int
}
// 定义切片类型,用于实现排序接口
type StudentSlice []Student
// 实现 sort.Interface 三个方法
func (s StudentSlice) Len() int { return len(s) }
func (s StudentSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// 排序规则:按分数降序
func (s StudentSlice) Less(i, j int) bool { return s[i].Score > s[j].Score }
func main() {
students := []Student{
{"张三", 85},
{"李四", 92},
{"王五", 78},
}
// 排序
sort.Sort(StudentSlice(students))
fmt.Println("按分数降序排序:")
for _, s := range students {
fmt.Printf("%s: %d分\n", s.Name, s.Score)
}
}
方案 2:使用 sort.Slice(极简写法,推荐)
Go 1.8+ 提供了 sort.Slice 函数,无需实现接口,直接传入排序规则,代码更简洁:
go
func main() {
students := []Student{
{"张三", 85},
{"李四", 92},
{"王五", 78},
}
// 一行代码排序:按分数升序
sort.Slice(students, func(i, j int) bool {
return students[i].Score < students[j].Score
})
fmt.Println("按分数升序排序:")
for _, s := range students {
fmt.Printf("%s: %d分\n", s.Name, s.Score)
}
}
四、零基础必看:sort 底层原理 + 为什么实现3个方法就能排序?
很多新手都会疑惑:为什么我只写了 Len、Less、Swap 三个方法,sort 就能帮我排序?
我用最通俗、零基础能懂的方式,把底层逻辑讲透。
1. 核心类比:sort 包 = 全自动排序机器人
你可以把 Go 的 sort 包想象成一个只会指挥排序流程的机器人:
- 它不知道你要排什么数据(学生、商品、水果都可以);
- 它不会自己判断大小;
- 它不会自己交换数据;
- 它只负责「循环比较、指挥排序」。
这个机器人只需要你告诉它 3 件事,就能完成排序:
- 一共有多少个数据?→ 对应
Len()方法 - 两个数据谁排在前面?→ 对应
Less()方法 - 两个数据怎么交换位置?→ 对应
Swap()方法
2. 3个方法的真实作用(直白解释)
go
// 1. 机器人问:有多少个数据要排?你回答长度
func (s StudentSlice) Len() int { return len(s) }
// 2. 机器人说:把第i个和第j个数据换位置!你执行交换
func (s StudentSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// 3. 机器人问:第i个数据应该排在第j个前面吗?你定规则
func (s StudentSlice) Less(i, j int) bool { return s[i].Score > s[j].Score }
重点拆解 Swap 交换:
s[i], s[j] = s[j], s[i] 是 Go 特色语法:先把右边两个值全部取出来,再一次性赋值给左边,直接完成两个元素的位置互换,不需要额外定义临时变量,一行就能交换成功。
3. sort 源码底层极简逻辑(看懂就通透)
Go sort 包的底层源码,根本不关心你排的是什么数据,它只做三件事:
go
// 简化后的 sort 核心源码
func Sort(data Interface) {
// 1. 调用你写的 Len(),知道数据长度
n := data.Len()
// 2. 循环比较(底层是优化后的快速排序)
for i := 1; i < n; i++ {
// 3. 调用你写的 Less(),判断谁在前谁在后
for j := i; j > 0 && data.Less(j, j-1); j-- {
// 4. 需要换位置时,调用你写的 Swap()
data.Swap(j, j-1)
}
}
}
4. 一句话总结底层逻辑
- 你负责:告诉排序机器人「长度、比较规则、交换方式」
- sort 包负责:执行排序流程
两者配合,就能对任何数据完成排序!
五、进阶用法:稳定排序 + 有序检查
1. 稳定排序
sort.Stable:排序后,相等元素保持原始顺序(适用于有优先级的场景)。
go
// 对整数切片做稳定升序排序
sort.Stable(sort.IntSlice(nums))
2. 检查切片是否有序
go
nums := []int{1,2,3,6}
// 检查整数是否升序
fmt.Println(sort.IntsAreSorted(nums)) // true
// 检查字符串是否有序
fmt.Println(sort.StringsAreSorted(strs))
六、完整示例:综合排序场景
go
package main
import (
"fmt"
"sort"
)
type Product struct {
Name string
Price float64
Stock int
}
func main() {
products := []Product{
{"手机", 5999.0, 100},
{"电脑", 8999.0, 50},
{"耳机", 399.0, 200},
}
// 按价格升序排序
sort.Slice(products, func(i, j int) bool {
return products[i].Price < products[j].Price
})
fmt.Println("=== 按价格排序 ===")
for _, p := range products {
fmt.Printf("%s: %.1f元\n", p.Name, p.Price)
}
}
七、总结
- 基础排序 :直接用
sort.Ints()、sort.Float64s()、sort.Strings(),一行搞定; - 结构体排序 :标准写法实现
Len、Less、Swap三个方法,极简写法用sort.Slice; - 底层核心:sort 是排序机器人,你提供规则,它执行流程,配合即可排序;
- 交换原理 :
s[i], s[j] = s[j], s[i]先取值再赋值,直接完成位置互换; - 进阶能力:支持逆序、稳定排序、有序性检查,满足所有开发场景。
Go 的 sort 包设计简洁、功能强大,完全覆盖日常开发的所有排序需求,是 Go 语言中最实用的标准库之一。