ZetCode

Golang slices.SortStableFunc

最后修改于 2025 年 4 月 20 日

本教程将介绍如何在 Go 中使用 slices.SortStableFunc 函数。我们将通过实际示例和自定义比较函数来讲解稳定排序。

slices.SortStableFunc 函数在排序切片时会保持相等元素的原始顺序。它是 Go 实验性 slices 包的一部分。

当您需要在排序后保持相等元素的相对顺序时,此函数非常有用。它接受一个自定义比较函数来进行排序。

基本的 SortStableFunc 示例

slices.SortStableFunc 最简单的用法是按升序对整数进行排序。我们定义一个返回 -1、0 或 1 的比较函数。

basic_sort.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{3, 1, 4, 1, 5, 9, 2, 6}
    
    slices.SortStableFunc(numbers, func(a, b int) int {
        if a < b {
            return -1
        }
        if a > b {
            return 1
        }
        return 0
    })
    
    fmt.Println("Sorted numbers:", numbers)
}

我们对整数切片进行排序,同时保持相等元素的顺序。比较函数遵循标准的“小于”约定。

按长度对字符串进行排序

slices.SortStableFunc 可以根据自定义标准对字符串进行排序。此示例按长度对字符串进行排序,同时保持相同长度的原始顺序。

string_sort.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    words := []string{"apple", "banana", "kiwi", "orange", "fig"}
    
    slices.SortStableFunc(words, func(a, b string) int {
        if len(a) < len(b) {
            return -1
        }
        if len(a) > len(b) {
            return 1
        }
        return 0
    })
    
    fmt.Println("Sorted by length:", words)
}

比较函数检查字符串长度而不是字典顺序。对于长度相等的字符串,将保留原始顺序。

按多个字段对结构体进行排序

我们可以将 slices.SortStableFunc 与自定义结构体类型一起使用。此示例按年龄对人员进行排序,对于年龄相同的人员,则按姓名排序。

struct_sort.go
package main

import (
    "fmt"
    "slices"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 25},
        {"David", 30},
    }
    
    slices.SortStableFunc(people, func(a, b Person) int {
        if a.Age < b.Age {
            return -1
        }
        if a.Age > b.Age {
            return 1
        }
        if a.Name < b.Name {
            return -1
        }
        if a.Name > b.Name {
            return 1
        }
        return 0
    })
    
    fmt.Println("Sorted people:")
    for _, p := range people {
        fmt.Printf("%s (%d)\n", p.Name, p.Age)
    }
}

比较函数首先检查年龄,然后检查相同年龄下姓名。稳定排序会保留具有相同年龄和姓名的个人的原始顺序。

不区分大小写的字符串排序

此示例演示了不区分大小写的排序,同时保留了在不区分大小写的情况下相等字符串的原始顺序。

case_insensitive.go
package main

import (
    "fmt"
    "slices"
    "strings"
)

func main() {
    words := []string{"Apple", "banana", "apple", "Banana", "cherry"}
    
    slices.SortStableFunc(words, func(a, b string) int {
        aLower := strings.ToLower(a)
        bLower := strings.ToLower(b)
        if aLower < bLower {
            return -1
        }
        if aLower > bLower {
            return 1
        }
        return 0
    })
    
    fmt.Println("Case-insensitive sorted:", words)
}

我们在比较前将字符串转换为小写。由于稳定排序,“Apple”和“apple”的原始顺序得到了保留。

降序排序

要按降序排序,我们只需反转比较逻辑。此示例将数字从最高到最低排序。

descending_sort.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{3, 1, 4, 1, 5, 9, 2, 6}
    
    slices.SortStableFunc(numbers, func(a, b int) int {
        if a > b {
            return -1
        }
        if a < b {
            return 1
        }
        return 0
    })
    
    fmt.Println("Descending sorted:", numbers)
}

比较函数在 a > b 时返回 -1 以实现降序。相等元素的原始相对位置将保持不变。

具有复杂逻辑的排序

slices.SortStableFunc 可以处理复杂的排序标准。此示例按长度对字符串进行排序,对于长度相等的字符串,则按字母顺序排序。

complex_sort.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    words := []string{"apple", "banana", "kiwi", "orange", "fig", "pear"}
    
    slices.SortStableFunc(words, func(a, b string) int {
        if len(a) < len(b) {
            return -1
        }
        if len(a) > len(b) {
            return 1
        }
        if a < b {
            return -1
        }
        if a > b {
            return 1
        }
        return 0
    })
    
    fmt.Println("Complex sorted:", words)
}

比较函数首先比较长度,然后对长度相等的字符串执行字典序比较。稳定排序会保留原始顺序。

实际示例:员工排序

这个实际示例按部门对员工进行排序,然后在部门内按薪资进行排序,在相等比较时保留原始顺序。

employee_sort.go
package main

import (
    "fmt"
    "slices"
)

type Employee struct {
    Name       string
    Department string
    Salary     int
}

func main() {
    employees := []Employee{
        {"Alice", "HR", 50000},
        {"Bob", "IT", 75000},
        {"Charlie", "HR", 50000},
        {"David", "IT", 80000},
        {"Eve", "Finance", 60000},
    }
    
    slices.SortStableFunc(employees, func(a, b Employee) int {
        if a.Department < b.Department {
            return -1
        }
        if a.Department > b.Department {
            return 1
        }
        if a.Salary < b.Salary {
            return -1
        }
        if a.Salary > b.Salary {
            return 1
        }
        return 0
    })
    
    fmt.Println("Sorted employees:")
    for _, e := range employees {
        fmt.Printf("%s: %s $%d\n", e.Name, e.Department, e.Salary)
    }
}

员工首先按部门名称排序,然后在每个部门内按薪资排序。对于具有相同字段的员工,将保留原始顺序。

来源

Go 实验性切片包文档

本教程通过使用自定义比较函数的稳定排序的实际示例,介绍了 Go 中的 slices.SortStableFunc 函数。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 Go 教程