ZetCode

Golang maps.Equal

最后修改于 2025 年 4 月 20 日

本教程将解释如何在 Go 中使用 `maps.Equal` 函数。我们将通过实际示例介绍 map 比较操作。

`maps.Equal` 函数用于测试两个 map 是否包含相同的键值对。它是 Go 实验性 maps 包的一部分。

此函数非常适合在不进行手动迭代的情况下比较 maps。只有当两个 maps 具有完全相同的键和值时,它才会返回 true。

基本的 maps.Equal 示例

`maps.Equal` 最简单的用法是比较两个具有字符串键的 map。为了被认为是相等的,两个 map 必须具有完全相同的键值对。

basic_equal.go
package main

import (
    "fmt"
    "maps"
)

func main() {
    m1 := map[string]int{"a": 1, "b": 2}
    m2 := map[string]int{"a": 1, "b": 2}
    
    equal := maps.Equal(m1, m2)
    fmt.Println("Maps are equal:", equal)
}

我们创建了两个相同的 map 并比较它们。由于两个 map 都包含相同的键值对(顺序不重要),因此函数返回 true。

比较顺序不同的 Map

`maps.Equal` 无论键的插入顺序如何,都会将 map 视为相等。此示例显示了创建顺序不同的 map 也是相等的。

order_independent.go
package main

import (
    "fmt"
    "maps"
)

func main() {
    m1 := map[string]int{"x": 10, "y": 20, "z": 30}
    m2 := map[string]int{"z": 30, "x": 10, "y": 20}
    
    equal := maps.Equal(m1, m2)
    fmt.Println("Order-independent equality:", equal)
}

这两个 map 包含相同的内容,但创建顺序不同。函数正确地将它们识别为相等。

不等检测

此示例演示了 `maps.Equal` 如何检测 map 之间的键或值差异。

inequality.go
package main

import (
    "fmt"
    "maps"
)

func main() {
    m1 := map[string]int{"apple": 3, "banana": 5}
    m2 := map[string]int{"apple": 3, "banana": 6}
    m3 := map[string]int{"apple": 3, "orange": 5}
    
    fmt.Println("Same keys, different values:", maps.Equal(m1, m2))
    fmt.Println("Different keys:", maps.Equal(m1, m3))
}

第一个比较失败是因为 "banana" 的值不同。第二个比较失败是因为键本身就不同。

比较空 Map

`maps.Equal` 可以正确处理空 map。nil map 和已初始化的空 map 都被认为是相等的。

empty_maps.go
package main

import (
    "fmt"
    "maps"
)

func main() {
    var m1 map[string]bool
    m2 := map[string]bool{}
    m3 := map[string]bool{"active": true}
    
    fmt.Println("Nil vs empty:", maps.Equal(m1, m2))
    fmt.Println("Empty vs non-empty:", maps.Equal(m2, m3))
}

未初始化的 nil map 和已初始化的空 map 是相等的。它们都与包含实际内容的 map 不相等。

比较包含结构体值的 Map

`maps.Equal` 可以比较包含结构体值的 map。为此,结构体必须是可比较的。

struct_values.go
package main

import (
    "fmt"
    "maps"
)

type Point struct {
    X, Y int
}

func main() {
    m1 := map[string]Point{
        "origin": {0, 0},
        "center": {5, 5},
    }
    
    m2 := map[string]Point{
        "origin": {0, 0},
        "center": {5, 5},
    }
    
    equal := maps.Equal(m1, m2)
    fmt.Println("Struct maps equal:", equal)
}

该函数逐个字段地比较结构体值。两个 map 包含相同的 Point 结构体,因此它们是相等的。

大型 Map 的性能

对于大型 map,`maps.Equal` 比手动比较更有效。此示例对性能差异进行了基准测试。

performance.go
package main

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

func manualEqual(m1, m2 map[int]int) bool {
    if len(m1) != len(m2) {
        return false
    }
    for k, v1 := range m1 {
        if v2, ok := m2[k]; !ok || v1 != v2 {
            return false
        }
    }
    return true
}

func main() {
    m1 := make(map[int]int, 100000)
    m2 := make(map[int]int, 100000)
    
    for i := 0; i < 100000; i++ {
        m1[i] = i * 2
        m2[i] = i * 2
    }
    
    start := time.Now()
    _ = maps.Equal(m1, m2)
    fmt.Println("maps.Equal duration:", time.Since(start))
    
    start = time.Now()
    _ = manualEqual(m1, m2)
    fmt.Println("Manual equal duration:", time.Since(start))
}

内置函数通常比手动实现更快。它针对此特定操作进行了优化。

实际示例:配置比较

这个实际示例比较配置 map 以检测更改。它对于需要跟踪状态的应用程序非常有用。

config_comparison.go
package main

import (
    "fmt"
    "maps"
)

func main() {
    currentConfig := map[string]interface{}{
        "timeout": 30,
        "retries": 3,
        "debug":   false,
    }
    
    newConfig := map[string]interface{}{
        "timeout": 30,
        "retries": 5,  // Changed value
        "debug":   false,
    }
    
    if maps.Equal(currentConfig, newConfig) {
        fmt.Println("No configuration changes detected")
    } else {
        fmt.Println("Configuration has changed")
    }
}

我们比较了包含混合类型的 `interface{}` map。该函数检测到 "retries" 值已更改并返回 false。

来源

Go 实验性 maps 包文档

本教程通过在各种场景下比较 map 的实际示例,介绍了 Go 中的 `maps.Equal` 函数。

作者

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

列出所有 Go 教程