Go 中 sort 包的排序接口
FreeGuideOnline
最新
2026-07-07
go type Interface interface { Len() int // 集合中的元素个数 Less(i, j int) bool // 索引 i 的元素是否应排在索引 j 的元素之前 Swap(i, j int) // 交换索引 i 和索引 j 的元素 }
只要你的数据结构支持这三个操作,就能轻松排序。
### 对内置切片排序
Go 为常见的内置类型提供了适配器类型:`sort.IntSlice`、`sort.StringSlice`、`sort.Float64Slice`,它们都实现了 `sort.Interface`,可直接用于排序。
```go
package main
import (
"fmt"
"sort"
)
func main() {
nums := []int{5, 2, 6, 3, 1, 4}
// 方法1:使用 sort.IntSlice 并调用其 Sort 方法
sort.IntSlice(nums).Sort()
fmt.Println(nums) // [1 2 3 4 5 6]
// 方法2:直接使用 sort.Ints (内部就是 sort.IntSlice 的 Sort)
sort.Ints(nums)
// sort.Strings() / sort.Float64s() 同理
}
对于降序排序,只需包装一下 Less 方法,或者使用 sort.Slice(后续讲解),或者反转已有的升序切片:
sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println(nums) // [6 5 4 3 2 1]
sort.Reverse 返回一个实现了 Interface 的类型,它调换原 Less 方法的逻辑。
对自定义结构体切片排序
假设我们有一个 Person 结构体切片,需要按年龄排序。
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 25},
{"Bob", 30},
{"Dave", 20},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Dave 20} {Alice 25} {Bob 30}]
}
如果希望支持多种排序顺序(先按年龄,再按姓名),可以这样实现:
type ByNameAge []Person
func (a ByNameAge) Len() int { return len(a) }
func (a ByNameAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// 先按姓名升序,姓名相同则按年龄升序
func (a ByNameAge) Less(i, j int) bool {
if a[i].Name == a[j].Name {
return a[i].Age < a[j].Age
}
return a[i].Name < a[j].Name
}
使用 sort.Slice 简化排序(Go 1.8+)
编写三个方法有时显得冗长,Go 1.8 引入了 sort.Slice,只需传入切片和一个比较函数即可。
people := []Person{
{"Alice", 25}, {"Bob", 30}, {"Dave", 20},
}
// 按年龄排序
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println(people)
它内部使用反射来获取长度并执行交换,因此对于小型切片性能影响不大,对性能极度敏感的场景仍建议实现 Interface。
对应的稳定排序版本是 sort.SliceStable,它会保持相等元素的原始相对顺序。
排序稳定性
sort.Sort 不保证稳定排序(相等元素的原始顺序可能改变)。如果需要稳定排序,使用 sort.Stable 而不是 sort.Sort,或使用 sort.SliceStable。
// 稳定排序:按年龄,但保持原本顺序
sort.Stable(ByAge(people))
// 或者
sort.SliceStable(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
自定义集合实现总结
实现排序的一般步骤:
- 为你的类型定义一个序列(通常是
[]YourType的别名)。 - 实现
Len()、Swap()和Less()三个方法。 - 调用
sort.Sort(YourType)或sort.Stable(YourType)进行排序。 - 对于简单场景,直接使用
sort.Slice更便捷。
常见问题与注意事项
- 关于
sort.Reverse:sort.Reverse不能直接用于基础类型的降序,需要包装成sort.Reverse(sort.IntSlice(nums))这样的形式,然后传给sort.Sort。对于简单降序,使用sort.Slice+ 反转比较逻辑更直观。 - 性能考量:
sort.Slice使用了反射,但通常不是瓶颈。只有在排序核心循环才建议手写Interface。 - 检查切片是否已排序:
sort.IntsAreSorted、sort.Float64sAreSorted、sort.StringsAreSorted可检查基础切片是否有序。
综合示例
下面展示一个完整的程序,包含多种排序方式的对比。
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Salary int
}
func main() {
// 1. 基础类型排序
ints := []int{3, 1, 4, 1, 5, 9}
sort.Ints(ints)
fmt.Println("ints sorted:", ints)
// 2. 自定义类型实现 Interface
emps := []Employee{
{"Alice", 500}, {"Bob", 450}, {"Carol", 600},
}
sort.Sort(BySalary(emps))
fmt.Println("by salary:", emps)
// 3. 使用 slice 排序
sort.Slice(emps, func(i, j int) bool {
return emps[i].Name < emps[j].Name
})
fmt.Println("by name:", emps)
// 4. 稳定排序演示
// 先按 Salary 排序,再稳定按 Name 排序
sort.SliceStable(emps, func(i, j int) bool {
return emps[i].Name < emps[j].Name
})
// 如果两组相等,它们会保持之前按 Salary 已经排好的相对顺序
}
type BySalary []Employee
func (s BySalary) Len() int { return len(s) }
func (s BySalary) Less(i, j int) bool { return s[i].Salary < s[j].Salary }
func (s BySalary) Swap(i, j int) { s[i], s[j] = s[j], s[i] }