ZetCode

Golang slices.MaxFunc

最后修改于 2025 年 4 月 20 日

本教程解释了如何在 Go 中使用 slices.MaxFunc 函数。我们将涵盖使用自定义比较函数查找最大元素。

slices.MaxFunc 函数根据自定义比较函数返回切片中的最大元素。它是 Go 实验性 slices 包的一部分。

当您需要根据自定义标准而不是自然排序来查找“最大”元素时,此函数非常有用。如果切片为空,它会 panic。

基本的 slices.MaxFunc 示例

slices.MaxFunc 最简单的用法是查找切片中的最大数字。我们提供一个比较两个整数的比较函数。

basic_max.go
package main

import (
    "fmt"
    "slices"
)

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

我们创建一个数字切片,并使用比较函数找到最大值。如果 a < b,则函数返回 -1;如果 a > b,则返回 1;如果它们相等,则返回 0。

查找最长的字符串

slices.MaxFunc 可以查找切片中最长的字符串。此示例通过字符串长度比较字符串。

longest_string.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    words := []string{"apple", "banana", "cherry", "date"}
    
    longest := slices.MaxFunc(words, func(a, b string) int {
        if len(a) < len(b) {
            return -1
        } else if len(a) > len(b) {
            return 1
        }
        return 0
    })
    
    fmt.Println("Longest word:", longest)
}

比较函数检查字符串长度而不是字典顺序。“banana”是返回的最长字符串,长度为 6 个字符。

使用结构体

我们可以将 slices.MaxFunc 与自定义结构类型一起使用。此示例查找切片中最年长的人。

struct_max.go
package main

import (
    "fmt"
    "slices"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 17},
    }
    
    oldest := slices.MaxFunc(people, func(a, b Person) int {
        if a.Age < b.Age {
            return -1
        } else if a.Age > b.Age {
            return 1
        }
        return 0
    })
    
    fmt.Println("Oldest person:", oldest)
}

该函数通过 Age 字段比较 Person 结构。Bob 是返回的最年长的人,年龄为 30 岁。

自定义比较逻辑

可以在函数中实现复杂的比较逻辑。此示例查找绝对值最大的数字。

absolute_max.go
package main

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

func main() {
    numbers := []int{-5, 2, -8, 3, 1}
    
    maxAbs := slices.MaxFunc(numbers, func(a, b int) int {
        absA := math.Abs(float64(a))
        absB := math.Abs(float64(b))
        
        if absA < absB {
            return -1
        } else if absA > absB {
            return 1
        }
        return 0
    })
    
    fmt.Println("Number with largest absolute value:", maxAbs)
}

比较函数首先计算绝对值,然后进行比较。返回 -8,因为它的绝对值最大(8)。

处理空切片

slices.MaxFunc 在空切片上调用时会 panic。此示例显示如何安全地处理这种情况。

empty_slice.go
package main

import (
    "fmt"
    "slices"
)

func main() {
    var empty []int
    
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    // This will panic
    _ = slices.MaxFunc(empty, func(a, b int) int {
        if a < b {
            return -1
        } else if a > b {
            return 1
        }
        return 0
    })
    
    fmt.Println("This line won't be reached")
}

我们使用 defer 和 recover 来优雅地处理 panic。在生产代码中调用 MaxFunc 之前,请务必检查切片长度。

按多个字段查找最大值

我们可以使用多个字段来实现复杂的比较逻辑。此示例查找得分最高的学生,以姓名作为平局决定因素。

multi_field.go
package main

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

type Student struct {
    Name  string
    Score int
}

func main() {
    students := []Student{
        {"Alice", 85},
        {"Bob", 92},
        {"Charlie", 85},
        {"Diana", 92},
    }
    
    topStudent := slices.MaxFunc(students, func(a, b Student) int {
        if a.Score < b.Score {
            return -1
        } else if a.Score > b.Score {
            return 1
        }
        // Tiebreaker: compare names alphabetically
        return strings.Compare(a.Name, b.Name)
    })
    
    fmt.Printf("Top student: %s with score %d\n", topStudent.Name, topStudent.Score)
}

当分数相同时,比较将回退到按字母顺序排列的姓名。Bob 是返回的最高分学生,在姓名比较中击败了 Diana。

实际示例:最新文件

这个实际示例查找目录中修改时间最近的文件。我们通过修改时间戳比较文件。

latest_file.go
package main

import (
    "fmt"
    "os"
    "slices"
    "time"
)

func main() {
    files := []string{"file1.txt", "file2.txt", "file3.txt"}
    
    fileInfos := make([]os.FileInfo, 0, len(files))
    for _, f := range files {
        fi, err := os.Stat(f)
        if err != nil {
            fmt.Printf("Error reading %s: %v\n", f, err)
            continue
        }
        fileInfos = append(fileInfos, fi)
    }
    
    if len(fileInfos) == 0 {
        fmt.Println("No valid files found")
        return
    }
    
    latest := slices.MaxFunc(fileInfos, func(a, b os.FileInfo) int {
        aTime := a.ModTime()
        bTime := b.ModTime()
        
        if aTime.Before(bTime) {
            return -1
        } else if aTime.After(bTime) {
            return 1
        }
        return 0
    })
    
    fmt.Printf("Latest file: %s (modified at %v)\n", latest.Name(), latest.ModTime())
}

我们首先收集文件信息,然后找到修改时间最新的文件。该示例会优雅地处理错误和空结果。

来源

Go 实验性切片包文档

本教程介绍了 Go 中的 slices.MaxFunc 函数,并提供了使用自定义比较函数查找最大元素的实际示例。

作者

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

列出所有 Go 教程