引入

Go time 包 能计算时间差,但如果有多个事件要按时间排列呢?Go 的 sort 包提供通用的排序功能,能对切片和自定义类型进行排序,是数据处理中的基本工具。

正文

定义

定义

sort 包提供排序和搜索功能。内置函数直接排序 int、float64、string 切片,sort.Slice 能对任意切片按自定义规则排序,sort.Search 实现二分搜索。

语法

import "sort"
 
// 内置排序
sort.Ints(a)           // 排序 int 切片
sort.Float64s(f)       // 排序 float64 切片
sort.Strings(s)        // 排序 string 切片
 
// 自定义排序
sort.Slice(slice, func(i, j int) bool {
    return slice[i] < slice[j]
})

例子

排序 int 切片

nums := []int{3, 1, 4, 1, 5, 9, 2, 6}
sort.Ints(nums)
fmt.Println(nums)  // [1 1 2 3 4 5 6 9]
 
// 是否已排序
fmt.Println(sort.IntsAreSorted(nums))  // true

sort.Ints 原地排序,直接修改原切片。sort.IntsAreSorted 检查是否已排序。

排序 string 切片

words := []string{"banana", "apple", "cherry"}
sort.Strings(words)
fmt.Println(words)  // [apple banana cherry]

sort.Strings 按字典序排序。

排序 float64 切片

scores := []float64{3.14, 2.71, 1.41, 1.73}
sort.Float64s(scores)
fmt.Println(scores)  // [1.41 1.73 2.71 3.14]

sort.Slice:自定义排序

nums := []int{3, 1, 4, 1, 5, 9}
 
// 降序排列
sort.Slice(nums, func(i, j int) bool {
    return nums[i] > nums[j]
})
fmt.Println(nums)  // [9 5 4 3 1 1]

sort.Slice 第二个参数是比较函数,返回 true 表示 i 排在 j 前面。想要降序就把 > 换成 <。

排序结构体切片

type User struct {
    Name string
    Age  int
}
 
users := []User{
    {"Tom", 20},
    {"Alice", 25},
    {"Bob", 18},
}
 
// 按年龄升序
sort.Slice(users, func(i, j int) bool {
    return users[i].Age < users[j].Age
})
// [Bob 18, Tom 20, Alice 25]
 
// 按名字字典序
sort.Slice(users, func(i, j int) bool {
    return users[i].Name < users[j].Name
})
// [Alice 25, Bob 18, Tom 20]

sort.Slice 最强大的地方就是能对任意切片按任意字段排序。比较函数里写排序逻辑就行。

稳定排序

type Event struct {
    Name string
    Day  int
}
 
events := []Event{
    {"A", 1}, {"B", 2}, {"C", 1}, {"D", 2},
}
 
sort.SliceStable(events, func(i, j int) bool {
    return events[i].Day < events[j].Day
})
// [A 1, C 1, B 2, D 2]  — 同 Day 保持原顺序

sort.Slice 不保证相等元素的顺序,sort.SliceStable 保证相等元素维持原始顺序。

二分搜索

nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
 
i := sort.SearchInts(nums, 5)
fmt.Println(i)  // 4(索引)
 
// 通用二分搜索
i = sort.Search(len(nums), func(k int) bool {
    return nums[k] >= 5
})
fmt.Println(i)  // 4

二分搜索要求切片已排序。sort.Search 返回第一个使条件为 true 的索引。

sort.Reverse

nums := []int{3, 1, 4, 1, 5}
sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println(nums)  // [5 4 3 1 1]

sort.Reverse 反转排序顺序。配合 sort.IntSlice、sort.StringSlice 等类型转换使用。

常见写法

写法说明
sort.Ints(a)排序 int 切片(升序)
sort.Strings(s)排序 string 切片
sort.Float64s(f)排序 float64 切片
sort.Slice(s, less)自定义排序任意切片
sort.SliceStable(s, less)稳定排序
sort.IntsAreSorted(a)检查是否已排序
sort.SearchInts(a, v)在 int 切片中二分搜索
sort.SearchStrings(s, v)在 string 切片中二分搜索
sort.Search(n, f)通用二分搜索
sort.Reverse(data)反转排序顺序

特点

  • sort.Ints / sort.Strings / sort.Float64s 是原地排序,直接修改原切片
  • sort.Slice 能对任意类型切片排序,比较函数决定排序规则
  • sort.SliceStable 保持相等元素的原始顺序,多级排序时需要
  • 二分搜索要求切片已排序,否则会返回错误结果
  • 排序是 O(n log n) 复杂度,大数据量也能接受
  • Go 1.21+ 引入了泛型版本 slices.Sort,写法更简洁

理解

sort 包的核心思路很简单:内置函数处理常见类型(int、string、float64),sort.Slice 一个函数搞定所有自定义排序。写比较函数就记住一点——返回 true 表示 i 应该排在 j 前面。升序用 <,降序用 >。SliceStable 在需要保持相对顺序时别用错了。

引出

排序经常配合文件数据使用——从文件读取数据、排序后写回去。接下来看 Go 文件操作,学习 Go 中读写文件的各种方式。