在Go语言开发中,处理数据集合时经常需要对结构体切片按照多个规则进行排序,比如先按某个字段升序,再按另一个字段降序,这种多维度排序需求在业务场景中十分常见。掌握对应的实现策略可以大幅提升开发效率。

核心依赖:sort包的基础能力
Go语言标准库的sort包提供了通用的排序接口,所有需要实现排序的集合类型都需要实现sort.Interface接口,该接口包含三个方法:
Len() int:返回集合的长度Less(i, j int) bool:定义排序的比较规则,当索引i的元素应该排在索引j的元素前面时返回trueSwap(i, j int):交换索引i和j位置的元素
多维度排序的核心逻辑就实现在Less方法中,通过依次判断多个排序字段的优先级来完成比较。
基础示例:定义结构体与排序规则
首先定义一个学生结构体,包含姓名、年龄、考试分数三个字段,我们需要实现先按年龄升序,年龄相同再按分数降序的排序规则。
package main
import (
"fmt"
"sort"
)
// 定义学生结构体
type Student struct {
Name string
Age int
Score int
}
// 定义学生切片类型,实现sort.Interface接口
type StudentSlice []Student
func (s StudentSlice) Len() int {
return len(s)
}
func (s StudentSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less方法实现多维度排序逻辑
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 main() {
// 初始化测试数据
students := StudentSlice{
{Name: "张三", Age: 18, Score: 90},
{Name: "李四", Age: 17, Score: 85},
{Name: "王五", Age: 18, Score: 95},
{Name: "赵六", Age: 17, Score: 92},
}
// 执行排序
sort.Sort(students)
// 打印排序结果
for _, stu := range students {
fmt.Printf("姓名:%s,年龄:%d,分数:%dn", stu.Name, stu.Age, stu.Score)
}
}
上述代码的输出结果会先按年龄从小到大排列,年龄相同的学生再按分数从高到低排列,符合我们预设的多维度排序规则。
进阶场景:更多维度的排序扩展
如果需要增加第三维度,比如年龄、分数都相同的情况下按姓名字典序升序,只需要在Less方法中继续添加判断逻辑即可。
// 扩展后的Less方法,支持三个维度排序
func (s StudentSlice) Less(i, j int) bool {
// 第一维度:年龄升序
if s[i].Age != s[j].Age {
return s[i].Age < s[j].Age
}
// 第二维度:年龄相同则按分数降序
if s[i].Score != s[j].Score {
return s[i].Score > s[j].Score
}
// 第三维度:年龄和分数都相同则按姓名字典序升序
return s[i].Name < s[j].Name
}
使用sort.Slice简化实现
Go 1.8之后新增了sort.Slice函数,不需要自定义切片类型实现接口,只需要传入比较函数即可,代码更加简洁。
package main
import (
"fmt"
"sort"
)
type Student struct {
Name string
Age int
Score int
}
func main() {
students := []Student{
{Name: "张三", Age: 18, Score: 90},
{Name: "李四", Age: 17, Score: 85},
{Name: "王五", Age: 18, Score: 95},
{Name: "赵六", Age: 17, Score: 92},
}
// 使用sort.Slice实现多维度排序
sort.Slice(students, func(i, j int) bool {
// 第一维度:年龄升序
if students[i].Age != students[j].Age {
return students[i].Age < students[j].Age
}
// 第二维度:年龄相同则按分数降序
if students[i].Score != students[j].Score {
return students[i].Score > students[j].Score
}
// 第三维度:按姓名字典序升序
return students[i].Name < students[j].Name
})
for _, stu := range students {
fmt.Printf("姓名:%s,年龄:%d,分数:%dn", stu.Name, stu.Age, stu.Score)
}
}
注意事项
- 多维度排序的优先级是从
Less函数的判断顺序从上到下依次生效的,先判断的字段优先级更高 - 如果排序的字段包含指针类型,需要先判断指针是否为空,避免出现空指针异常
- 对于需要稳定排序的场景,可以使用
sort.SliceStable函数,相等元素的相对顺序会保持不变
多维度排序的核心是把多个排序规则按优先级依次写入比较逻辑中,无论是自定义接口实现还是使用sort.Slice,本质都是定义好元素之间的先后比较规则。