Golang 迭代器模式

114 阅读1分钟

The Iterator Design Pattern is collection dependent. For example, if you are given a List:

List={1,2,3,4,5,6}

you may traverse in this way:

list := []int{1, 2, 3, 4, 5, 6}
for _, v := range list {
	println(v)
}

We don't need to know whether the underlying layer of the list is an Array or a Linked List.

But if I give a Set

Set={4,3,2,1,5,6}

  • how do you traverse it?
  • Do you need to know the underlying data structure to traverse?
  • Is there a way to handle all collections with a unified type of interface?

At this time, the Iterator Design Pattern is used

iterator.go

package designpattern

type Iterator[V any] interface {
	HasNext() bool
	Next() V
}

type ArrayList[V any] struct {
	data []V
}

func NewArrayList[V any](v ...V) ArrayList[V] {
	return ArrayList[V]{data: v}
}

func (array *ArrayList[V]) Iterator() *ArrayListIterator[V] {
	return &ArrayListIterator[V]{currIndex: 0, list: array}
}

type ArrayListIterator[V any] struct {
	currIndex int
	list      *ArrayList[V]
}

func (iterator *ArrayListIterator[V]) HasNext() bool {
	return len(iterator.list.data) > iterator.currIndex
}

func (iterator *ArrayListIterator[V]) Next() V {
	if len(iterator.list.data) > iterator.currIndex {
		v := iterator.list.data[iterator.currIndex]
		iterator.currIndex++
		return v
	} else {
		var v V
		return v
	}

}

type HashSet[V comparable] struct {
	data map[V]bool
}

func NewHashSet[V comparable](v ...V) HashSet[V] {
	mapPool := make(map[V]bool)
	for _, element := range v {
		if _, ok := mapPool[element]; !ok {
			mapPool[element] = true
		}
	}
	return HashSet[V]{data: mapPool}
}

func (hashSet *HashSet[V]) Iterator() *HashSetIterator[V] {
	keys := make([]V, 0, len(hashSet.data))
	for k := range hashSet.data {
		keys = append(keys, k)
	}
	return &HashSetIterator[V]{currIndex: 0, set: keys}
}

type HashSetIterator[V any] struct {
	currIndex int
	set       []V
}

func (iterator *HashSetIterator[V]) HasNext() bool {
	return len(iterator.set) > iterator.currIndex
}

func (iterator *HashSetIterator[V]) Next() V {
	if len(iterator.set) > iterator.currIndex {
		v := iterator.set[iterator.currIndex]
		iterator.currIndex++
		return v
	} else {
		var v V
		return v
	}

}

iterator_test.go

package designpattern_test

import (
	designpattern "ptarmigan-golang-design-pattern/src"
	"testing"
)

func TestArrayIterator(t *testing.T) {
	array := designpattern.NewArrayList(1, 2, 3, 4, 5, 6)
	iterator := array.Iterator()
	var iteratorCount = 6
	index := 0

	for iterator.HasNext() {
		println(iterator.Next())
		index++
	}

	if index != iteratorCount {
		t.Error("Iterator needs 6 iterations")
	}

	if iterator.HasNext() || 0 != iterator.Next() {
		t.Error("Iterator has not finished iteration")
	}

}

func TestHashSetIterator(t *testing.T) {
	hashSet := designpattern.NewHashSet(1, 2, 3, 4, 5, 6)
	iterator := hashSet.Iterator()
	var iteratorCount = 6
	index := 0

	for iterator.HasNext() {
		println(iterator.Next())
		index++
	}

	if index != iteratorCount {
		t.Error("Iterator needs 6 iterations")
	}

	if iterator.HasNext() || 0 != iterator.Next() {
		t.Error("Iterator has not finished iteration")
	}

}

func TestArray(t *testing.T) {
	list := []int{1, 2, 3, 4, 5, 6}
	for _, v := range list {
		println(v)
	}
}