Go设计模式:Iterator
最常见的迭代模式莫过于循环:
package main
func main() {
for i := 0; i < 10; i++ {
println(i)
}
}
迭代器会依次把元素呈现给我们,当没有元素时,迭代终止。
那么Go语言里还有哪些常见的迭代器呢?比如 sync.Map
里,就有:
package main
import (
"fmt"
"sync"
)
func main() {
aMap := sync.Map{}
aMap.Store("hello", "world")
aMap.Store("world", "hello")
aMap.Range(func(k, v interface{}) bool {
fmt.Printf("key: %s, value %s\n", k, v)
return true
})
}
我们来看看这个 Range 是怎么实现的:
func (m *Map) Range(f func(key, value interface{}) bool) {
// We need to be able to iterate over all of the keys that were already
// present at the start of the call to Range.
// If read.amended is false, then read.m satisfies that property without
// requiring us to hold m.mu for a long time.
read, _ := m.read.Load().(readOnly)
if read.amended {
// m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
// (assuming the caller does not break out early), so a call to Range
// amortizes an entire copy of the map: we can promote the dirty copy
// immediately!
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
if read.amended {
read = readOnly{m: m.dirty}
m.read.Store(read)
m.dirty = nil
m.misses = 0
}
m.mu.Unlock()
}
for k, e := range read.m {
v, ok := e.load()
if !ok {
continue
}
if !f(k, v) {
break
}
}
}
// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {
m map[interface{}]*entry
amended bool // true if the dirty map contains some key not in m.
}
其中 read.m
是一个map,所以还是使用Go内置的map来做的,那如果我们想自己做一个该咋办呢?我们来参考
golang-set 的实现方式:
先看用法:
package main
import (
"fmt"
mapset "github.com/deckarep/golang-set"
)
func main() {
aSet := mapset.NewSet()
aSet.Add("hello")
aSet.Add("world")
aSet.Add("hello")
aSet.Add("world")
for v := range aSet.Iterator().C {
fmt.Printf("v: %s\n", v.(string))
}
}
来看看实现:
type Iterator struct {
C <-chan interface{}
stop chan struct{}
}
func (set *threadSafeSet) Iterator() *Iterator {
iterator, ch, stopCh := newIterator()
go func() {
set.RLock()
L:
for elem := range set.s {
select {
case <-stopCh:
break L
case ch <- elem:
}
}
close(ch)
set.RUnlock()
}()
return iterator
}
可以看出来,其实它也是通过底层的for循环,加channel来实现的,我们自己来实现一个:
package main
import (
"fmt"
)
// Iterator 声明接口
type Iterator interface {
Iterator(m iterSet) Iter
}
// Iter 迭代器的实现
type Iter struct {
C chan interface{}
}
func newIter(i *iterSet) Iter {
iter := Iter{make(chan interface{})}
go func() {
for k := range i.m {
iter.C <- k
}
close(iter.C)
}()
return iter
}
// 我们自己的set
type iterSet struct {
m map[string]bool
}
// Add 添加元素
func (i *iterSet) Add(k string) {
i.m[k] = true
}
// Iterator 返回一个迭代器
func (i *iterSet) Iterator() Iter {
return newIter(i)
}
func main() {
aSet := iterSet{map[string]bool{}}
aSet.Add("hello")
aSet.Add("hello")
aSet.Add("world")
aSet.Add("world")
iter := aSet.Iterator()
for v := range iter.C {
fmt.Printf("key: %s\n", v.(string))
}
}
duang,搞定!
参考资料:
更多文章
本站热门
- socks5 协议详解
- zerotier简明教程
- 搞定面试中的系统设计题
- frp 源码阅读与分析(一):流程和概念
- 用peewee代替SQLAlchemy
- Golang(Go语言)中实现典型的fork调用
- DNSCrypt简明教程
- 一个Gunicorn worker数量引发的血案
- Golang validator使用教程
- Docker组件介绍(二):shim, docker-init和docker-proxy
- Docker组件介绍(一):runc和containerd
- 使用Go语言实现一个异步任务框架
- 协程(coroutine)简介 - 什么是协程?
- SQLAlchemy简明教程
- Go Module 简明教程