之前给了基于go的自定义Stream实现,一直觉得比较变扭,和java自定义的实现风格也不太一致,
随着对go语法的进一步理解,参考之前写的java自定义Stream实现逻辑,对go 的Stream实现进行了优化
步骤一:函数定义
通过go 的type关键字对Stream相关函数进行定义,方便后续使用
//定义Consumer函数类型
type Consumer[T any] func(t T)
//定义Predicate函数类型
type Predicate[T any] func(t T) bool
//定义Convert行数类型(对应java的Function)
type Convert[T any] func(t T) T
//定义Stream函数类型
type Stream[T any] func(c Consumer[T])
对于type Stream[T any] func(c Consumer[T]) ,和java实现方式中的Pipeline接口等价,是一切的开始
步骤二:提供Stream初始化方法
func OfStream[T any](arr []T) Stream[T] {
var stream Stream[T] = func(c Consumer[T]) {
for _, v := range arr {
c(v)
}
}
return stream
}
这里是示例基于arr []T产生Stream,你也可以基于自己的诉求来构建这个Stream,例如可以实现从文件中读数据参数流,从数据库中读数据参数流,从网络中读数据参数流等等
步骤三:提供通用的Map函数
func (s Stream[T]) Map(cv Convert[T]) Stream[T] {
var stream Stream[T] = func(c Consumer[T]) {
s(func(t T) {
c(cv(t))
})
}
return stream
}
代码实现上,重新构建了一个Stream函数,新的Stream函数的实现上,会调用原来的Stream函数,原来的函数需要一个Consumer函数参数,(func(t T) {c(cv(t))},就是Consumer的实例,逻辑上就是把数据map后再给到新Stream的Consumer函数进行消费
步骤四:提供Filter函数
func (s Stream[T]) Filter(test Predicate[T]) Stream[T] {
var stream Stream[T] = func(c Consumer[T]) {
s(func(t T) {
if test(t) {
c(t)
}
})
}
return stream
}
理解了Map函数的逻辑后,Filter的逻辑应该就比较好理解,就是过滤符合条件的数据给新Stream函数的Consumer消费
步骤五:提供Reduce函数
func (s Stream[T]) ToSlice() []T {
result := make([]T, 0)
s(func(t T) {
result = append(result, t)
}
return result
}
以上是一个reduce函数实现示例,将结果放到Slice中
测试
package main
import (
"fmt"
"gotest/main/algorithm"
)
func main() {
stream := algorithm.OfStream[int]([]int{1, 2, 3, 4, 5, 6})
r := stream.
Map(func(t int) int { return t + 1 }). //2,3,4,5,6,7
Map(func(t int) int { return t * 3 }). //6,9,12,,15,18,21
Filter(func(t int) bool { return t%2 == 0 }). //6,12,18
ToSlice()
fmt.Println(r)
}
//输出结果:[6 12 18]
总结
通过上面的实现,就和java的实现方式很接近了,只要掌握了原理,不同语言的实现主要是语法上的差异,逻辑上应该是一致的。