Go 中 sort 包的排序接口

FreeGuideOnline 最新 2026-07-07

go type Interface interface { Len() int // 集合中的元素个数 Less(i, j int) bool // 索引 i 的元素是否应排在索引 j 的元素之前 Swap(i, j int) // 交换索引 i 和索引 j 的元素 }


只要你的数据结构支持这三个操作,就能轻松排序。

### 对内置切片排序
Go 为常见的内置类型提供了适配器类型:`sort.IntSlice`、`sort.StringSlice`、`sort.Float64Slice`,它们都实现了 `sort.Interface`,可直接用于排序。

```go
package main

import (
    "fmt"
    "sort"
)

func main() {
    nums := []int{5, 2, 6, 3, 1, 4}
    // 方法1:使用 sort.IntSlice 并调用其 Sort 方法
    sort.IntSlice(nums).Sort()
    fmt.Println(nums) // [1 2 3 4 5 6]

    // 方法2:直接使用 sort.Ints (内部就是 sort.IntSlice 的 Sort)
    sort.Ints(nums)
    // sort.Strings() / sort.Float64s() 同理
}

对于降序排序,只需包装一下 Less 方法,或者使用 sort.Slice(后续讲解),或者反转已有的升序切片:

sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println(nums) // [6 5 4 3 2 1]

sort.Reverse 返回一个实现了 Interface 的类型,它调换原 Less 方法的逻辑。

对自定义结构体切片排序

假设我们有一个 Person 结构体切片,需要按年龄排序。

type Person struct {
    Name string
    Age  int
}

type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 30},
        {"Dave", 20},
    }
    sort.Sort(ByAge(people))
    fmt.Println(people)
    // [{Dave 20} {Alice 25} {Bob 30}]
}

如果希望支持多种排序顺序(先按年龄,再按姓名),可以这样实现:

type ByNameAge []Person

func (a ByNameAge) Len() int      { return len(a) }
func (a ByNameAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }

// 先按姓名升序,姓名相同则按年龄升序
func (a ByNameAge) Less(i, j int) bool {
    if a[i].Name == a[j].Name {
        return a[i].Age < a[j].Age
    }
    return a[i].Name < a[j].Name
}

使用 sort.Slice 简化排序(Go 1.8+)

编写三个方法有时显得冗长,Go 1.8 引入了 sort.Slice,只需传入切片和一个比较函数即可。

people := []Person{
    {"Alice", 25}, {"Bob", 30}, {"Dave", 20},
}
// 按年龄排序
sort.Slice(people, func(i, j int) bool {
    return people[i].Age < people[j].Age
})
fmt.Println(people)

它内部使用反射来获取长度并执行交换,因此对于小型切片性能影响不大,对性能极度敏感的场景仍建议实现 Interface

对应的稳定排序版本是 sort.SliceStable,它会保持相等元素的原始相对顺序。

排序稳定性

sort.Sort 不保证稳定排序(相等元素的原始顺序可能改变)。如果需要稳定排序,使用 sort.Stable 而不是 sort.Sort,或使用 sort.SliceStable

// 稳定排序:按年龄,但保持原本顺序
sort.Stable(ByAge(people))
// 或者
sort.SliceStable(people, func(i, j int) bool {
    return people[i].Age < people[j].Age
})

自定义集合实现总结

实现排序的一般步骤:

  1. 为你的类型定义一个序列(通常是 []YourType 的别名)。
  2. 实现 Len()Swap()Less() 三个方法。
  3. 调用 sort.Sort(YourType)sort.Stable(YourType) 进行排序。
  4. 对于简单场景,直接使用 sort.Slice 更便捷。

常见问题与注意事项

  • 关于 sort.Reversesort.Reverse 不能直接用于基础类型的降序,需要包装成 sort.Reverse(sort.IntSlice(nums)) 这样的形式,然后传给 sort.Sort。对于简单降序,使用 sort.Slice + 反转比较逻辑更直观。
  • 性能考量sort.Slice 使用了反射,但通常不是瓶颈。只有在排序核心循环才建议手写 Interface
  • 检查切片是否已排序sort.IntsAreSortedsort.Float64sAreSortedsort.StringsAreSorted 可检查基础切片是否有序。

综合示例

下面展示一个完整的程序,包含多种排序方式的对比。

package main

import (
    "fmt"
    "sort"
)

type Employee struct {
    Name   string
    Salary int
}

func main() {
    // 1. 基础类型排序
    ints := []int{3, 1, 4, 1, 5, 9}
    sort.Ints(ints)
    fmt.Println("ints sorted:", ints)

    // 2. 自定义类型实现 Interface
    emps := []Employee{
        {"Alice", 500}, {"Bob", 450}, {"Carol", 600},
    }
    sort.Sort(BySalary(emps))
    fmt.Println("by salary:", emps)

    // 3. 使用 slice 排序
    sort.Slice(emps, func(i, j int) bool {
        return emps[i].Name < emps[j].Name
    })
    fmt.Println("by name:", emps)

    // 4. 稳定排序演示
    // 先按 Salary 排序,再稳定按 Name 排序
    sort.SliceStable(emps, func(i, j int) bool {
        return emps[i].Name < emps[j].Name
    })
    // 如果两组相等,它们会保持之前按 Salary 已经排好的相对顺序
}

type BySalary []Employee

func (s BySalary) Len() int           { return len(s) }
func (s BySalary) Less(i, j int) bool { return s[i].Salary < s[j].Salary }
func (s BySalary) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }