Go 的 sort 包设计得非常优雅和灵活,它不仅仅是一个函数,而是一整套工具,其核心是基于一个接口 (sort.Interface) 来实现的。
我们从最简单的用法开始,逐步深入到其核心设计。
1. 基础用法:为基本类型切片排序
对于 Go 内置的几种基本数据类型,sort 包提供了非常方便的函数,你无需做任何额外工作即可直接使用。
sort.Ints(slice): 对[]int切片进行升序排序。sort.Float64s(slice): 对[]float64切片进行升序排序。sort.Strings(slice): 对[]string切片进行字典序升序排序。
这些函数都是原地排序,意味着它们会直接修改传入的切片,而不是返回一个新的排好序的切片。
2. 核心用法:sort.Sort() 和 sort.Interface (排序任意数据)
如果你想排序一个自定义结构体的切片,比如按照用户的年龄或姓名排序,就需要用到 sort 包最核心的功能。
Go 的 sort.Sort(data Interface) 函数可以排序任何满足 sort.Interface 接口的数据类型。这个接口定义了排序算法所需要知道的三个基本操作:
type Interface interface {
// Len 是集合中的元素个数
Len() int
// Less 报告索引 i 的元素是否应该排在索引 j 的元素之前
Less(i, j int) bool
// Swap 交换索引 i 和 j 的元素
Swap(i, j int)
}
步骤如下:
创建一个你的自定义类型的切片。
为这个切片类型定义
Len(),Less(i, j int), 和Swap(i, j int)这三个方法。调用
sort.Sort()函数,将你的切片实例传入。
3. 便捷用法:sort.Slice() (更现代的方式)
实现完整的 sort.Interface 有时候会显得有些繁琐。从 Go 1.8 开始,sort 包提供了一个更便捷的函数 sort.Slice。你不需要定义新类型或实现三个方法,只需要提供一个比较函数(通常是一个闭包)。
func Slice(slice any, less func(i, j int) bool)
slice: 你想要排序的切片。less: 一个函数,它定义了slice[i]是否应该排在slice[j]前面。
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
{"David", 20},
}
fmt.Println("原始数据:", people)
// --- 场景1: 按年龄升序排序 ---
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println("按年龄升序:", people)
// --- 场景2: 按姓名(字典序)排序 ---
sort.Slice(people, func(i, j int) bool {
return people[i].Name < people[j].Name
})
fmt.Println("按姓名升序:", people)
}
sort.Slice 极其灵活,因为你可以随时定义不同的 less 函数来对同一个数据结构进行多种维度的排序,而无需创建多个类型。
4. 其他有用的 sort 函数
sort.IsSorted(data Interface): 检查一个集合是否已经排好序。对于基础类型,也有sort.IntsAreSorted()等函数。sort.Search(n int, f func(int) bool): 在一个已排序的集合中进行二分查找。它会找到满足f(i)为true的最小索引i。常用于在有序数据中高效地查找某个值。
sort.Search 要求你所搜索的数据范围必须是“有序的”。这里的“有序”指的是,对于你要判断的条件 f,这个范围内的索引可以被分为两部分:
前面一部分索引,
f(i)的结果全部是false。后面一部分索引,
f(i)的结果全部是true。
sort.Search 的任务是找到那个从 false 变成 true 的临界点,也就是第一个让 f(i) 返回 true 的索引 i。
func Search(n int, f func(int) bool) int
n int: 定义了搜索范围是[0, n)。通常,如果你在切片s中搜索,n就是len(s)。f func(int) bool: 这是一个“断言函数”或“判定函数”。你提供一个函数f,它接受一个索引i作为参数,并返回true或false。这个函数定义了你的搜索条件。
如果元素不存在,sort.Search 会返回这个元素应该被插入的位置,以保持切片的有序性。
对于基本类型,sort 包提供了一些方便的包装函数,你无需自己编写闭包。
sort.SearchInts(s []int, x int)sort.SearchFloat64s(s []float64, x float64)sort.SearchStrings(s []string, x string)
sort.SearchInts(s, x) 的内部实现就等同于 sort.Search(len(s), func(i int) bool { return s[i] >= x })。