在Golang的标准库中,sort包提供了通用的排序功能,能够满足大多数场景下的数据排序需求,不需要开发者自行实现复杂的排序算法。它基于快速排序、堆排序、插入排序等算法的组合实现,会自动根据数据规模选择最合适的排序策略。

基础类型排序
sort包针对int、float64、string这三种基础类型提供了对应的排序函数,分别是sort.Ints、sort.Float64s、sort.Strings,这些函数会对输入的切片进行原地升序排序。
package main
import (
"fmt"
"sort"
)
func main() {
// 整数切片排序
intSlice := []int{5, 2, 8, 1, 9}
sort.Ints(intSlice)
fmt.Println("整数排序结果:", intSlice)
// 浮点数切片排序
floatSlice := []float64{3.14, 1.59, 2.65, 0.99}
sort.Float64s(floatSlice)
fmt.Println("浮点数排序结果:", floatSlice)
// 字符串切片排序,按Unicode码点升序
strSlice := []string{"banana", "apple", "cherry", "date"}
sort.Strings(strSlice)
fmt.Println("字符串排序结果:", strSlice)
}
判断切片是否已排序
如果只需要判断切片是否已经有序,不需要执行排序操作,可以使用sort.IntsAreSorted、sort.Float64sAreSorted、sort.StringsAreSorted这三个函数,返回值为bool类型。
package main
import (
"fmt"
"sort"
)
func main() {
orderedSlice := []int{1, 2, 3, 4, 5}
unorderedSlice := []int{5, 3, 1, 2, 4}
fmt.Println("orderedSlice是否已排序:", sort.IntsAreSorted(orderedSlice))
fmt.Println("unorderedSlice是否已排序:", sort.IntsAreSorted(unorderedSlice))
}
自定义排序规则
当需要对自定义结构体或者非升序的规则排序时,需要实现sort.Interface接口,该接口包含三个方法:
Len() int:返回集合的长度Less(i, j int) bool:判断索引i的元素是否小于索引j的元素,返回true表示i位置的元素应该排在j位置前面Swap(i, j int):交换索引i和j位置的元素
自定义结构体排序
比如我们有一个学生结构体,需要按照年龄升序排序,如果年龄相同则按照分数降序排序,实现方式如下:
package main
import (
"fmt"
"sort"
)
// 定义学生结构体
type Student struct {
Name string
Age int
Score float64
}
// 定义学生切片类型,实现sort.Interface接口
type StudentSlice []Student
func (s StudentSlice) Len() int {
return len(s)
}
func (s StudentSlice) Less(i, j int) bool {
// 先按年龄升序
if s[i].Age != s[j].Age {
return s[i].Age < s[j].Age
}
// 年龄相同按分数降序
return s[i].Score > s[j].Score
}
func (s StudentSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func main() {
students := StudentSlice{
{Name: "张三", Age: 18, Score: 90.5},
{Name: "李四", Age: 17, Score: 88.0},
{Name: "王五", Age: 18, Score: 95.0},
{Name: "赵六", Age: 17, Score: 92.5},
}
fmt.Println("排序前:")
for _, stu := range students {
fmt.Printf("姓名:%s 年龄:%d 分数:%.1fn", stu.Name, stu.Age, stu.Score)
}
// 执行排序
sort.Sort(students)
fmt.Println("n排序后:")
for _, stu := range students {
fmt.Printf("姓名:%s 年龄:%d 分数:%.1fn", stu.Name, stu.Age, stu.Score)
}
}
使用sort.Slice简化自定义排序
Golang 1.8之后新增了sort.Slice函数,不需要显式定义类型实现接口,直接传入比较函数即可完成排序,使用起来更加简洁。
package main
import (
"fmt"
"sort"
)
type Student struct {
Name string
Age int
Score float64
}
func main() {
students := []Student{
{Name: "张三", Age: 18, Score: 90.5},
{Name: "李四", Age: 17, Score: 88.0},
{Name: "王五", Age: 18, Score: 95.0},
{Name: "赵六", Age: 17, Score: 92.5},
}
// 使用sort.Slice排序,less函数定义排序规则
sort.Slice(students, func(i, j int) bool {
if students[i].Age != students[j].Age {
return students[i].Age < students[j].Age
}
return students[i].Score > students[j].Score
})
fmt.Println("排序后:")
for _, stu := range students {
fmt.Printf("姓名:%s 年龄:%d 分数:%.1fn", stu.Name, stu.Age, stu.Score)
}
}
稳定排序
上面的sort.Sort和sort.Slice都是不稳定排序,即相等元素在排序后的相对位置可能发生变化。如果需要稳定排序,可以使用sort.Stable或者sort.SliceStable函数,相等元素的原始顺序会被保留。
package main
import (
"fmt"
"sort"
)
func main() {
// 相同元素的不稳定排序
slice1 := []int{2, 1, 2, 3, 1}
sort.Slice(slice1, func(i, j int) bool {
return slice1[i] < slice1[j]
})
fmt.Println("不稳定排序结果:", slice1)
// 相同元素的稳定排序
slice2 := []int{2, 1, 2, 3, 1}
sort.SliceStable(slice2, func(i, j int) bool {
return slice2[i] < slice2[j]
})
fmt.Println("稳定排序结果:", slice2)
}
注意事项
- sort包的排序函数都是原地排序,会直接修改原切片的内容,如果需要保留原数据,排序前需要先拷贝切片。
Less方法的定义决定了排序的顺序,返回true表示i位置元素排在j位置前面,如果需要降序排序,只需要将比较逻辑反过来即可。- 对nil切片或者空切片执行排序操作不会报错,会直接返回空结果。
- sort包的函数不是线程安全的,如果需要在并发场景下使用,需要自行添加同步机制。