引入

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

正文

定义

定义

sort 包提供排序和搜索功能。内置函数直接排序 intfloat64string 切片,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.IntSlicesort.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 包的核心思路很简单:内置函数处理常见类型(intstringfloat64),sort.Slice 一个函数搞定所有自定义排序。写比较函数就记住一点——返回 true 表示 i 应该排在 j 前面。升序用 <,降序用 >SliceStable 在需要保持相对顺序时别用错了。

引出

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