如何在Go中创建一流的地图迭代器?

时间:2017-04-04 16:46:07

标签: dictionary go iterator

我正在编写一个迭代地图中条目的函数。我想能够干净地处理在迭代时从地图中添加或删除的项目,如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迭代器没有此功能!)

4 个答案:

答案 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
}

See running example on play.

答案 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
}