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)
}