如何在Go语言中清空一个map?

119

我正在寻找类似于C++函数.clear()的东西,用于基本类型map

或者我应该创建一个新的map呢?

更新:感谢您的回答。通过查看答案,我刚刚意识到有时创建一个新的map可能会导致一些我们不想要的不一致性。考虑以下示例:

var a map[string]string
var b map[string]string

func main() {
    a = make(map[string]string)
    b=a
    a["hello"]="world"
    a = nil
    fmt.Println(b["hello"])
}

我的意思是,这与C ++中的.clear()函数仍然有所不同,后者将清除对象中的内容。


1
请参阅以下讨论:https://groups.google.com/d/topic/golang-nuts/6yHDC7IYCj4/discussion - perreal
7个回答

160

你最好创建一个新的映射表。没有必要费力去尝试清空现有的映射表,除非同一个映射表被多个代码片段引用,且一个代码片段需要明确地清除值,以使其他代码片段能看到这种变化。

因此,你应该只是简单地创建一个新的映射表

mymap = make(map[keytype]valtype)
如果你真的需要清除现有地图,无论出于什么原因,这很简单:
for k := range m {
    delete(m, k)
}

1
那么逐个删除元素是唯一的方法吗? - lavin
谢谢您先生! :) 我认为随着我在 Go 中编写更多的代码,我会喜欢这种风格。 - lavin
11
在遍历地图中的所有值时修改地图的内容真的可以吗?其他编程语言可能无法正确运行。 - John Jeffery
7
在我发布它之前,我进行了测试。看起来可以使用。规范中的实际语言是“地图上的迭代顺序未指定,并且不能保证从一次迭代到下一次迭代相同。如果在迭代期间删除尚未到达的地图条目,则不会生成相应的迭代值。如果在迭代过程中插入地图条目,则行为取决于实现,但每个条目的迭代值最多产生一次。如果地图为空,则迭代次数为0。” 这表明其得到支持。 - Lily Ballard
7
编译器优化的更新版本说明链接如下: https://golang.org/doc/go1.11#performance-compiler - Nathan Baulch
显示剩余4条评论

33

与C++不同,Go是一种垃圾回收语言。你需要用稍微不同的方式来思考。

当你创建一个新的映射时

a := map[string]string{"hello": "world"}
a = make(map[string]string)

原始地图最终将被垃圾回收,您不需要手动清除它。但请记住,地图(和切片)是引用类型;您可以使用make()创建它们。只有当没有对底层映射的引用时,才会对其进行垃圾回收。

a := map[string]string{"hello": "world"}
b := a
a = make(map[string]string)

原始数组不会被垃圾回收(直到b被垃圾回收或b引用其他内容)。


10
与C++不同,Go是一种垃圾回收语言。你需要以稍微不同的方式思考问题。Java、Python和C#都具有“清晰”的特点,它们也都是垃圾回收语言:D - Spidey

16

Go 1.21

使用内置的 clear

func main() {
    m := map[string]int{"foo": 1}
    clear(m)
}

这是一个单个函数调用,具有次要但重要的优点,它将删除不可反射键(有关详细信息,请参阅下文)。标准库的maps包不再具有maps.Clear

Go 1.18 到 1.20

您可以使用maps.Clear。该函数属于golang.org/x/exp/maps包(实验性质,不受兼容性保证的覆盖)

Clear会从m中移除所有条目,使其变为空。

示例用法:

func main() {
    testMap := map[string]int{"gopher": 1, "badger": 2}
    maps.Clear(testMap)
    fmt.Println(testMap)
    
    testMap["zebra"] = 2000
    fmt.Println(testMap)
}

操场:https://go.dev/play/p/qIdnGrd0CYs?v=gotip 如果你不想依赖实验性的包,你可以复制粘贴源代码,实际上非常简单:
func Clear[M ~map[K]V, K comparable, V any](m M) {
    for k := range m {
        delete(m, k)
    }
}

重要提示:就像内置的delete一样,maps.Clear的实现也不会从地图中删除非自反键。原因是对于非自反键,根据定义,x == x为假。非自反键是NaN浮点数和其他支持比较运算符但包含NaN浮点数的类型。

请查看以下代码以了解其含义:

func main() {
    m := map[float64]string{}
    m[1.0] = "foo"

    k := math.NaN()
    fmt.Println(k == k) // false
    m[k] = "bar"

    maps.Clear(m)
    fmt.Printf("len: %d, content: %v\n", len(m), m) 
    // len: 1, content: map[NaN:bar]

    a := map[[2]float64]string{}
    a[[2]float64{1.0, 2.0}] = "foo"

    h := [2]float64{1.0, math.NaN()}
    fmt.Println(h == h) // false
    a[h] = "bar"

    maps.Clear(a)
    fmt.Printf("len: %d, content: %v\n", len(a), a) 
    // len: 1, content: map[[1 NaN]:bar]
}

操场:https://go.dev/play/p/LWfiD3iPA8Q

10
// Method - I , say book is name of map
for k := range book {
    delete(book, k)
}

// Method - II
book = make(map[string]int)

// Method - III
book = map[string]int{}

2
方法二和方法三不会清除地图。如果书的副本存在于某个地方,那么行为是不同的。例如,请参见 https://go.dev/play/p/H0VqSTkTC6m - AJR
2
方法二和方法三不能清除地图。如果书的副本存在于某个地方,那么行为就会有所不同。例如,请参见https://go.dev/play/p/H0VqSTkTC6m。 - undefined

1
清理围棋地图的方法
for k := range m {
    delete(m, k)
}

只有当m中不包含任何包含NaN的键值时才有效。

delete(m, k)对于任何非自反键(例如math.NaN()),以及包含任何NaN浮点数的结构体或其他可比较类型也无效。给定struct{ val float64 }valNaN也是非自反的(引用自blackgreen的评论)。


为了解决这个问题并支持在Go中清除地图,新版本中可以提供一个名为clear(x)的函数。有关更多详细信息,请参考添加clear(x)内置函数以清除地图、零切片内容和指向数组的指针
根据Go 1.21发布说明

新函数clear可删除地图中的所有元素或将切片中的所有元素置零。

调用 参数类型 结果
clear(m) map[K]T 删除所有条目,使得映射为空(len(m) == 0)
clear(s) []T 将所有元素设置为类型 T 的零值,直到 s 的长度
clear(t) 类型参数 见下文

如果 clear 的参数类型是类型参数,则其类型集中的所有类型必须是映射或切片,并且清除操作执行与实际类型参数相对应的操作。

如果映射或切片为 nil,则 clear 不执行任何操作。


1
这个答案可能更准确,delete(m, k) 对于任何不可自反的键都不起作用,这包括NaN浮点数,但也包括具有任何NaN浮点数的结构体或其他可比类型,例如带有NaN valstruct {val float64} 也是不可自反的,或者[2]float64 {1,math.NaN()} - blackgreen

0

Go 1.21 Release 在语言中添加了三个新的内置函数。具体来说:

  • 新函数clear可以删除映射中的所有元素或将切片中的所有元素置零。

它的工作原理如下:

m := map[string]string{"hello": "world"}
clear(m)
fmt.Println(m) // map[]

https://go.dev/play/p/UdQBUEeQ4ck?v=gotip


-8
如果您正在尝试在循环中执行此操作,您可以利用初始化来为您清除地图。例如:
for i:=0; i<2; i++ {
    animalNames := make(map[string]string)
    switch i {
        case 0:
            animalNames["cat"] = "Patches"
        case 1:
            animalNames["dog"] = "Spot";
    }

    fmt.Println("For map instance", i)
    for key, value := range animalNames {
        fmt.Println(key, value)
    }
    fmt.Println("-----------\n")
}

当您执行此操作时,它会清除先前的地图,并从空地图开始。这可以通过输出进行验证:

$ go run maptests.go 
For map instance 0
cat Patches
-----------

For map instance 1
dog Spot
-----------

4
这并不是清空地图,而是每次循环创建一个新的地图并将其绑定到具有相同名称的本地变量上。 - Delaney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接