我正在编写一个迭代地图中条目的函数。我想能够干净地处理在迭代时从地图中添加或删除的项目,如for k, v := range myMap { //...
,但我每次迭代只处理一个键/值对,因此我无法使用范围。我想要这样的东西:
func processItem(i iterator) bool {
k, v, ok := i.next()
if(!ok) {
return false
}
process(v)
return true
}
var m = make(map[string]widget)
// ...
i := makeIterator(m)
for processItem(i) {
// code which might add/remove item from m here
}
我知道该范围正在使用' hiter
'结构和相关函数,如src/runtime/hashmap.go
中所定义,用于执行迭代。有没有办法获得对这个迭代器的访问作为一个具体的(第一类)Go对象?
是否存在迭代地图的替代策略,该策略可以很好地处理插入/删除但是会给出第一类迭代器对象?
奖金问题:是否有一种替代策略,可以迭代地图,也处理地图,迭代器被序列化到磁盘然后恢复,迭代继续从哪里停下来? (显然内置的range
迭代器没有此功能!)
答案 0 :(得分:3)
你不能:(
迭代map
的唯一方法是使用for range
,你无法从中获取迭代器对象。
答案 1 :(得分:1)
您可以将频道用作迭代器。
你的迭代器将是一个返回通道的函数,该通道将当前迭代值传递给接收它的人:
func iterator(m map[string]widget) chan iteration {
c := make(chan iteration)
go func() {
for k,v := range m {
c <- iteration{k,v}
}
close(c)
}()
return c
}
这当然不是通用的,您可以使用interface{}
和/或反射使其通用,但如果您确实需要它,那应该不会太难。
在迭代结束时关闭通道将通知迭代结束,稍后将演示。
iteration
类型就在那里,所以你可以同时发送密钥和值,它看起来像这样:
type iteration struct {
key string
value widget
}
然后你可以这样做(on play):
m := map[string]widget{"foo": widget{3}, "bar": widget{4}}
i := iterator(m)
iter, ok := <- i
fmt.Println(iter, ok)
iter, ok = <- i
fmt.Println(iter, ok)
iter, ok = <- i
fmt.Println(iter, ok)
产生
{foo {3}} true
{bar {4}} true
{ {0}} false
答案 2 :(得分:1)
一种非常简单的方法是获取映射中所有键的列表,并将列表和映射打包在迭代器结构中。当我们想要下一个密钥时,我们从未从地图中删除的列表中选择下一个密钥:
type iterator struct {
m map[string]widget
keys []string
}
func newIterator(m map[string]widget) *iterator {
it := iterator{m, make([]string, len(m))}
i := 0
for k, _ := range m {
it.keys[i] = k
i++
}
return &it
}
func (it *iterator) next() (string, widget, bool) {
for len(it.keys) > 0 {
k := it.keys[0]
it.keys = it.keys[1:]
if _, exists := it.m[k]; exists {
return k, it.m[k], true
}
}
return "", widget{0}, false
}
答案 3 :(得分:0)
您可以定义自己的地图类型。解决并发问题也很好:
type ConcurrentMap struct {
sync.RWMutex
items map[string]interface{}
}
type ConcurrentMapItem struct {
Key string
Value interface{}
}
func (cm *ConcurrentMap) Iter() <-chan ConcurrentMapItem {
c := make(chan ConcurrentMapItem)
f := func() {
cm.Lock()
defer cm.Unlock()
for k, v := range cm.items {
c <- ConcurrentMapItem{k, v}
}
close(c)
}
go f()
return c
}