ZetCode

Golang maps.EqualFunc

最后修改于 2025 年 4 月 20 日

本教程解释了如何在 Go 中使用 maps.EqualFunc 函数。我们将通过使用自定义相等函数进行实际示例来涵盖 map 比较。

maps.EqualFunc 函数使用自定义函数比较两个 map 的相等性。它是 Go 实验性 maps 包的一部分。

当您需要对 map 值进行自定义比较逻辑时,此函数非常有用。只有当两个 map 具有相同的键和等效值时,它才返回 true。

基本的 maps.EqualFunc 示例

maps.EqualFunc 最简单的用法是比较具有不区分大小写字符串值的 map。我们定义一个自定义比较函数。

basic_equal.go
package main

import (
    "fmt"
    "maps"
    "strings"
)

func main() {
    m1 := map[string]string{"name": "Alice", "role": "Admin"}
    m2 := map[string]string{"name": "alice", "role": "admin"}
    
    equal := maps.EqualFunc(m1, m2, func(v1, v2 string) bool {
        return strings.EqualFold(v1, v2)
    })
    
    fmt.Println("Maps equal (case-insensitive):", equal)
}

我们比较了两个具有不同字符串大小写的 map。自定义函数使用 strings.EqualFold 进行不区分大小写的比较。

使用容差比较数值

maps.EqualFunc 可以使用容差范围比较数值。此示例检查浮点值是否近似相等。

numeric_tolerance.go
package main

import (
    "fmt"
    "maps"
    "math"
)

func main() {
    m1 := map[string]float64{"x": 1.23456, "y": 2.34567}
    m2 := map[string]float64{"x": 1.23455, "y": 2.34568}
    
    equal := maps.EqualFunc(m1, m2, func(v1, v2 float64) bool {
        return math.Abs(v1-v2) < 0.0001
    })
    
    fmt.Println("Maps equal (with tolerance):", equal)
}

比较函数检查浮点值之间的差异是否小于 0.0001。这种方法对于浮点数比较很有用,因为精确相等很少见。

比较自定义结构值

我们可以将 maps.EqualFunc 与自定义结构类型一起使用。此示例比较包含结构值的 map,这些结构值按特定字段进行比较。

struct_comparison.go
package main

import (
    "fmt"
    "maps"
)

type Product struct {
    ID    int
    Name  string
    Price float64
}

func main() {
    m1 := map[int]Product{
        1: {ID: 1, Name: "Laptop", Price: 999.99},
        2: {ID: 2, Name: "Mouse", Price: 19.99},
    }
    
    m2 := map[int]Product{
        1: {ID: 1, Name: "Laptop", Price: 999.99},
        2: {ID: 2, Name: "Mouse", Price: 19.98},
    }
    
    equal := maps.EqualFunc(m1, m2, func(p1, p2 Product) bool {
        return p1.ID == p2.ID && p1.Name == p2.Name
    })
    
    fmt.Println("Maps equal (ignoring price):", equal)
}

比较函数仅检查 ID 和 Name 字段,而忽略 Price 的差异。这演示了复杂类型的选择性字段比较。

比较不同键类型的 map

当使用转换时,maps.EqualFunc 可以比较不同键类型的 map。此示例比较字符串和 int 键的 map。

different_key_types.go
package main

import (
    "fmt"
    "maps"
    "strconv"
)

func main() {
    m1 := map[string]int{"one": 1, "two": 2, "three": 3}
    m2 := map[int]int{1: 1, 2: 2, 3: 3}
    
    // Convert m2 to string keys for comparison
    m2Converted := make(map[string]int)
    for k, v := range m2 {
        m2Converted[strconv.Itoa(k)] = v
    }
    
    equal := maps.EqualFunc(m1, m2Converted, func(v1, v2 int) bool {
        return v1 == v2
    })
    
    fmt.Println("Maps equal after conversion:", equal)
}

我们在比较之前将 int 键的 map 转换为字符串键。值使用标准相等性进行比较。这表明了 map 比较的灵活性。

空映射行为

maps.EqualFunc 特别处理空 map。此示例演示了其在各种空 map 组合下的行为。

empty_maps.go
package main

import (
    "fmt"
    "maps"
)

func main() {
    m1 := map[string]int{}
    m2 := map[string]int{}
    m3 := map[string]int{"a": 1}
    
    // Two empty maps
    equal1 := maps.EqualFunc(m1, m2, func(v1, v2 int) bool {
        return v1 == v2
    })
    
    // Empty vs non-empty
    equal2 := maps.EqualFunc(m1, m3, func(v1, v2 int) bool {
        return v1 == v2
    })
    
    fmt.Println("Two empty maps equal:", equal1)
    fmt.Println("Empty vs non-empty equal:", equal2)
}

具有相同键类型的空 map 始终相等。空 map 和非空 map 永远不相等,无论使用什么比较函数。

性能注意事项

对于大型 map,比较函数的性能很重要。此示例对不同的比较方法进行了基准测试。

performance.go
package main

import (
    "fmt"
    "maps"
    "time"
)

func main() {
    m1 := make(map[int]string, 1000000)
    m2 := make(map[int]string, 1000000)
    
    for i := 0; i < 1000000; i++ {
        m1[i] = fmt.Sprintf("value%d", i)
        m2[i] = fmt.Sprintf("value%d", i)
    }
    
    // Simple comparison
    start := time.Now()
    _ = maps.EqualFunc(m1, m2, func(v1, v2 string) bool {
        return v1 == v2
    })
    fmt.Println("Simple comparison:", time.Since(start))
    
    // Modified map
    m2[500000] = "modified"
    start = time.Now()
    _ = maps.EqualFunc(m1, m2, func(v1, v2 string) bool {
        return v1 == v2
    })
    fmt.Println("Early termination:", time.Since(start))
}

执行时间取决于 map 大小以及何时找到差异。maps.EqualFunc 为了效率,会在第一个不相等对处停止。

实际示例:配置比较

这个实际示例比较了具有特殊默认值处理的配置 map。它演示了实际用法。

config_comparison.go
package main

import (
    "fmt"
    "maps"
)

func main() {
    defaultConfig := map[string]interface{}{
        "timeout":     30,
        "retries":     3,
        "concurrent":  false,
        "log_level":   "info",
    }
    
    userConfig := map[string]interface{}{
        "timeout":     30,
        "retries":     3,
        "log_level":   "info",
    }
    
    equal := maps.EqualFunc(defaultConfig, userConfig, func(v1, v2 interface{}) bool {
        // Missing key in userConfig is treated as default
        if v2 == nil {
            return true
        }
        return v1 == v2
    })
    
    fmt.Println("Configs equivalent:", equal)
}

我们将用户配置与默认值进行比较,将缺失的键视为等同于默认值。自定义函数特殊处理 nil 值。

来源

Go 实验性 maps 包文档

本教程通过各种场景下使用自定义相等逻辑比较 map 的实际示例,介绍了 Go 中的 maps.EqualFunc 函数。

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Go 教程