Dictionary 在范围循环内从地图中删除选定的关键点安全吗?

Dictionary 在范围循环内从地图中删除选定的关键点安全吗?,dictionary,for-loop,go,Dictionary,For Loop,Go,如何从地图中删除选定的关键点? 将delete() package main import "fmt" type Info struct { value string } func main() { table := make(map[string]*Info) for i := 0; i < 10; i++ { str := fmt.Sprintf("%v", i) table[str] = &Info{str}

如何从地图中删除选定的关键点? 将
delete()

package main

import "fmt"

type Info struct {
    value string
}

func main() {
    table := make(map[string]*Info)

    for i := 0; i < 10; i++ {
        str := fmt.Sprintf("%v", i)
        table[str] = &Info{str}
    }

    for key, value := range table {
        fmt.Printf("deleting %v=>%v\n", key, value.value)
        delete(table, key)
    }
}
主程序包
输入“fmt”
类型信息结构{
值字符串
}
func main(){
表:=make(映射[字符串]*Info)
对于i:=0;i<10;i++{
str:=fmt.Sprintf(“%v”,i)
表[str]=&Info{str}
}
对于键,值:=范围表{
fmt.Printf(“删除%v=>%v\n”,键,value.value)
删除(表、键)
}
}

这是安全的!您还可以在以下内容中找到类似的示例:

以及:

映射上的迭代顺序没有指定,也不能保证从一次迭代到下一次迭代的顺序相同。如果在迭代过程中删除了尚未到达的映射项,则不会生成相应的迭代值。如果映射条目是在迭代过程中创建的,则该条目可能在迭代过程中生成,也可能被跳过。对于创建的每个条目,以及从一个迭代到下一个迭代,选择可能会有所不同。如果贴图为零,则迭代次数为0


塞巴斯蒂安的回答是准确的,但我想知道为什么它是安全的,所以我做了一些深入研究。它看起来像是在调用
delete(k,v)
时,它基本上只是设置一个标志(以及更改计数值),而不是实际删除值:

b->tophash[i] = Empty;
(空是值
0
的常量)

映射实际所做的似乎是根据映射的大小分配一组存储桶,当您以
2^B
(from)的速率执行插入时,存储桶的数量会增加:

因此,分配的存储桶几乎总是多于您使用的存储桶,当您在地图上执行
范围时,它会检查
tophash
中每个存储桶的
2^B
值,以查看是否可以跳过它

总之,在
范围内的
删除
是安全的,因为数据在技术上仍然存在,但是当它检查
tophash
时,它发现它可以跳过它,而不包括在您正在执行的任何
范围
操作中。源代码甚至包括一个
TODO

 // TODO: consolidate buckets if they are mostly empty
 // can only consolidate if there are no live iterators at this size.
这解释了为什么使用
delete(k,v)
函数实际上并没有释放内存,只是将其从允许访问的存储桶列表中删除。如果要释放实际内存,需要使整个映射不可访问,以便垃圾收集介入。您可以使用以下命令行执行此操作:

map = nil

我想知道是否会发生内存泄漏。所以我写了一个测试程序:

package main

import (
    log "github.com/Sirupsen/logrus"
    "os/signal"
    "os"
    "math/rand"
    "time"
)

func main() {
    log.Info("=== START ===")
    defer func() { log.Info("=== DONE ===") }()

    go func() {
        m := make(map[string]string)
        for {
            k := GenerateRandStr(1024)
            m[k] = GenerateRandStr(1024*1024)

            for k2, _ := range m {
                delete(m, k2)
                break
            }
        }
    }()

    osSignals := make(chan os.Signal, 1)
    signal.Notify(osSignals, os.Interrupt)
    for {
        select {
        case <-osSignals:
            log.Info("Recieved ^C command. Exit")
            return
        }
    }
}

func GenerateRandStr(n int) string {
    rand.Seed(time.Now().UnixNano())
    const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
    }
    return string(b)
}
主程序包
进口(
日志“github.com/Sirupsen/logrus”
“操作系统/信号”
“操作系统”
“数学/兰德”
“时间”
)
func main(){
log.Info(“==开始==”)
defer func(){log.Info(“==DONE===”)}()
go func(){
m:=make(映射[字符串]字符串)
为了{
k:=发电机DSTR(1024)
m[k]=生成器DSTR(1024*1024)
对于k2,1:=范围m{
删除(m,k2)
打破
}
}
}()
骨化信号:=make(变化信号,1)
信号通知(信号、操作系统中断)
为了{
挑选{

简言之,是的。参见前面的答案

还有,来自:

ianlancetaylor于2015年2月18日发表评论
我认为理解这一点的关键是要认识到,在执行for/range语句的主体时,没有当前的迭代。有一组已看到的值,还有一组未看到的值。在执行主体时,已看到的其中一个键/值对(最近的一对)被分配给变量range语句的able。该键/值对没有什么特别之处,它只是迭代过程中已经看到的键/值对之一


他回答的问题是关于在
range
操作过程中修改地图元素,这就是他提到“当前迭代”的原因。但这也与此相关:您可以在某个范围内删除关键点,这只意味着您以后不会在该范围内看到它们(如果您已经看到它们,那没关系).

听起来你是说从地图上删除任意值是安全的,而不仅仅是“当前”值,对吗?当需要计算我之前任意删除的哈希值时,它会安全地跳过它?@Flimzy这是正确的,正如你在这个游乐场上看到的。请注意,如果你删除的索引是first 1在范围内,它仍然会显示该索引,因为它已经写入k,v,但是如果将索引设置为除第一个索引之外的任何索引,则该范围找到的索引将只显示两个键/值对,而不是三个键/值对,并且不会死机。是“实际上没有释放内存”吗仍然相关?我试图在源代码中找到该注释,但找不到。重要提示:请记住,这只是当前的实现,将来可能会更改,因此您不能依赖它可能“支持”的任何其他属性。您仅有的保证是规范提供的。(这就是说,探索和解释Go内部结构确实很有趣,很有教育意义,而且通常也很棒!)key.expired未定义(type string没有字段或方法过期)@kristen——在上述示例中,键不应该是字符串,而应该是实现
func(a T)expired()的某个自定义类型bool
接口。在本例中,您可以尝试:
m:=make(map[int]int)
/*以某种方式在此处填充m*/
键:=range(m){
如果键%2==0{/*这只是一些条件,例如调用expired*/
delete(m,key);
非常混乱。
map = nil
package main

import (
    log "github.com/Sirupsen/logrus"
    "os/signal"
    "os"
    "math/rand"
    "time"
)

func main() {
    log.Info("=== START ===")
    defer func() { log.Info("=== DONE ===") }()

    go func() {
        m := make(map[string]string)
        for {
            k := GenerateRandStr(1024)
            m[k] = GenerateRandStr(1024*1024)

            for k2, _ := range m {
                delete(m, k2)
                break
            }
        }
    }()

    osSignals := make(chan os.Signal, 1)
    signal.Notify(osSignals, os.Interrupt)
    for {
        select {
        case <-osSignals:
            log.Info("Recieved ^C command. Exit")
            return
        }
    }
}

func GenerateRandStr(n int) string {
    rand.Seed(time.Now().UnixNano())
    const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    b := make([]byte, n)
    for i := range b {
        b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
    }
    return string(b)
}