ZetCode

Golang slices.SortedStableFunc

最后修改于 2025 年 4 月 20 日

本教程解释了如何在 Go 中使用 `slices.SortedStableFunc` 函数。我们将通过实际示例介绍使用自定义比较函数进行稳定排序。

`slices.SortedStableFunc` 函数使用自定义比较函数检查切片是否已排序,同时保留相等元素的顺序。

此函数是 Go 实验性 slices 包的一部分,当您需要在排序后维护相等元素的相对顺序时非常有用。

SortedStableFunc 基本示例

`slices.SortedStableFunc` 最简单的用法是使用自定义比较函数检查整数切片是否按升序排序。

basic_sorted.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{1, 2, 2, 3, 4, 5}
    
    isSorted := slices.SortedStableFunc(numbers, func(a, b int) int {
        return a - b
    })
    
    fmt.Println("Is slice sorted:", isSorted)
}

我们创建一个包含重复值的整数切片,并检查它是否已排序。比较函数返回一个负数、零或正数,指示顺序。

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

`slices.SortedStableFunc` 可以验证不区分大小写的字符串排序。此示例检查字符串是否在忽略大小写的情况下排序。

string_sorting.go
package main

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

func main() {
    words := []string{"apple", "Banana", "cherry", "Date"}
    
    isSorted := slices.SortedStableFunc(words, func(a, b string) int {
        return strings.Compare(strings.ToLower(a), strings.ToLower(b))
    })
    
    fmt.Println("Is slice sorted case-insensitively:", isSorted)
}

比较函数在比较之前将字符串转换为小写。这确保了检查不区分大小写,同时保持稳定性。

使用结构体

我们可以将 `slices.SortedStableFunc` 与自定义结构体类型一起使用。此示例检查人员是否按年龄排序,同时保持顺序。

struct_sorting.go
package main

import (
    "fmt"
    "slices"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 25},
        {"Charlie", 30},
    }
    
    isSorted := slices.SortedStableFunc(people, func(a, b Person) int {
        return a.Age - b.Age
    })
    
    fmt.Println("Are people sorted by age:", isSorted)
}

该函数检查切片是否按年龄排序,同时为相同年龄的人保留原始顺序。Alice 和 Bob 保持他们相对的位置。

降序检查

`slices.SortedStableFunc` 可以通过反转比较逻辑来验证降序。此示例演示了降序整数排序。

descending_order.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    numbers := []int{5, 4, 3, 2, 2, 1}
    
    isSorted := slices.SortedStableFunc(numbers, func(a, b int) int {
        return b - a // Reverse comparison for descending order
    })
    
    fmt.Println("Is slice sorted descending:", isSorted)
}

通过用 `a - b` 替换 `b - a`,我们反转了比较逻辑。这会检查降序,同时为相等值保持稳定性。

空切片和单元素切片

`slices.SortedStableFunc` 对空切片和单元素切片有特殊行为。这些情况始终被视为已排序。

edge_cases.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    empty := []int{}
    single := []string{"alone"}
    
    emptySorted := slices.SortedStableFunc(empty, func(a, b int) int {
        return a - b
    })
    
    singleSorted := slices.SortedStableFunc(single, func(a, b string) int {
        return len(a) - len(b)
    })
    
    fmt.Println("Empty slice sorted:", emptySorted)
    fmt.Println("Single-element sorted:", singleSorted)
}

空切片和单元素切片都是显然已排序的,因为没有元素可以比较,或者只有一个元素。比较函数无关紧要。

自定义比较逻辑

可以在函数中实现复杂的比较逻辑。此示例检查数字是否按绝对值排序,同时保留原始顺序。

custom_comparison.go
package main

import (
    "fmt"
    "math"
    "slices"
)

func main() {
    numbers := []int{-3, -2, -1, 0, 1, 1, 2, 3}
    
    isSorted := slices.SortedStableFunc(numbers, func(a, b int) int {
        absA := math.Abs(float64(a))
        absB := math.Abs(float64(b))
        if absA < absB {
            return -1
        }
        if absA > absB {
            return 1
        }
        return 0
    })
    
    fmt.Println("Sorted by absolute value:", isSorted)
}

比较函数首先计算绝对值,然后再进行比较。相等的绝对值在切片中保持其原始相对顺序。

实际示例:版本排序

这个实际示例检查版本字符串是否正确排序,处理版本比较的数字和稳定性方面。

version_sorting.go
package main

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

func main() {
    versions := []string{"1.0", "1.1", "1.1.1", "1.2", "1.10"}
    
    isSorted := slices.SortedStableFunc(versions, func(a, b string) int {
        aParts := strings.Split(a, ".")
        bParts := strings.Split(b, ".")
        
        for i := 0; i < len(aParts) && i < len(bParts); i++ {
            aNum, _ := strconv.Atoi(aParts[i])
            bNum, _ := strconv.Atoi(bParts[i])
            
            if aNum != bNum {
                return aNum - bNum
            }
        }
        
        return len(aParts) - len(bParts)
    })
    
    fmt.Println("Are versions sorted correctly:", isSorted)
}

比较函数会分割版本字符串并逐个比较数字组件。这确保了正确的版本排序(1.10 在 1.2 之后),同时保持稳定性。

来源

Go 实验性切片包文档

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

作者

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

列出所有 Go 教程