1.使用面向对象的方式写
package stream
import (
"fmt"
"log"
"reflect"
"sort"
"strconv"
"strings"
)
type Stream[T any] struct {
data []T
keyBy string
sortByNum string
sortByStr []string
}
func FromElement[T any](data []T) *Stream[T] {
return &Stream[T]{
data: data,
}
}
// 过滤算子
type filterfunc[F any] func(F) bool
func (s *Stream[T]) Filter(filterFun filterfunc[T]) *Stream[T] {
var new []T
for _, item := range s.data {
isfiltered := filterFun(item)
if isfiltered {
continue
}
new = append(new, item)
}
s.data = new
return s
}
// 单行处理
type mapfunc[F any] func(F) F
func (s *Stream[T]) Map(mapFun mapfunc[T]) *Stream[T] {
for idx, item := range s.data {
ret := mapFun(item)
s.data[idx] = ret
}
return s
}
// 排序
func (s *Stream[T]) SortByNum(key string) *Stream[T] {
s.sortByNum = key
if len(s.sortByStr) > 0 {
s.sortByStr = nil
}
return s
}
// 每次排序只能使用一种排
func (s *Stream[T]) SortByStr(keys ...string) *Stream[T] {
s.sortByStr = keys
if s.sortByNum != "" {
s.sortByNum = ""
}
return s
}
func (s *Stream[T]) Sort(esc bool) *Stream[T] {
if s.sortByNum == "" && len(s.sortByStr) == 0 {
log.Println("please call SortBy() before sort()")
return s
}
if s.sortByNum != "" {
sort.Slice(s.data, func(i, j int) bool {
v := reflect.ValueOf(s.data[i]).Elem()
field := v.FieldByName(s.sortByNum)
if !field.IsValid() {
log.Panicf("field=%s not valid", s.sortByNum)
}
idata := fmt.Sprintf("%v", field.Interface())
num, err := strconv.ParseInt(idata, 10, 64)
if err != nil {
log.Panic("please use num when use sortByNum", idata)
}
v1 := reflect.ValueOf(s.data[j]).Elem()
field1 := v1.FieldByName(s.sortByNum)
if !field1.IsValid() {
log.Panicf("field=%s not valid", s.sortByNum)
}
jdata := fmt.Sprintf("%v", field1.Interface())
num1, err := strconv.ParseInt(jdata, 10, 64)
if err != nil {
log.Panic("please use num when use sortByNum")
}
if esc {
return num < num1
} else {
return num > num1
}
})
}
if len(s.sortByStr) > 0 {
sort.Slice(s.data, func(i, j int) bool {
var ifinalv, jfinalv string
for _, key := range s.sortByStr {
v := reflect.ValueOf(s.data[i]).Elem()
field := v.FieldByName(key)
if !field.IsValid() {
log.Panicf("field=%s not valid", key)
}
idata := fmt.Sprintf("%v", field.Interface())
ifinalv = ifinalv + idata
}
for _, key := range s.sortByStr {
v := reflect.ValueOf(s.data[j]).Elem()
field := v.FieldByName(key)
if !field.IsValid() {
log.Panicf("field=%s not valid", key)
}
jdata := fmt.Sprintf("%v", field.Interface())
jfinalv = jfinalv + jdata
}
// i 大于j的话 返回1 所以正序需要返回false
ret := strings.Compare(ifinalv, jfinalv)
if esc {
return ret < 0
}
return ret >= 0
})
}
return s
}
// 设置聚合的key
func (s *Stream[T]) KeyBy(key string) *Stream[T] {
s.keyBy = key
return s
}
// reduce
// 暂时木有办法改变输出的结构
type reducefunc[F any] func([]F) F
func (s *Stream[T]) Reduce(reduceFun reducefunc[T]) *Stream[T] {
if s.keyBy == "" {
log.Fatal("please call keyby() before reduce()")
return nil
}
var cache = make(map[string][]T)
defer func() {
cache = nil
}()
for _, item := range s.data {
v := reflect.ValueOf(item).Elem()
field := v.FieldByName(s.keyBy)
key := field.String()
lis, ok := cache[key]
if !ok {
lis = make([]T, 0)
}
lis = append(lis, item)
cache[key] = lis
}
var new []T
for _, lis := range cache {
ret := reduceFun(lis)
new = append(new, ret)
}
s.data = new
return s
}
// 返回个数
func (s *Stream[T]) Limit(n int) []T {
if n > len(s.data) {
n = len(s.data)
}
return s.data[0:n]
}
func (s *Stream[T]) Print() {
for idx, item := range s.data {
log.Printf("idx=%d val=%v", idx, item)
}
}
func (s *Stream[T]) Result() []T {
return s.data
}
测试例子
func TestTostream(t *testing.T) {
FromElement([]*Student{
&Student{"xyf", "数学", 101},
&Student{"xyf", "语文", 108},
&Student{"xyf", "外语", 101},
}).Map(func(st *Student) *Student {
st.Score = st.Score + 10
return st
}).Filter(func(st *Student) bool {
return st.Name == "xyf"
}).
// SortByStr("Name", "Subject").
SortByNum("Score").
Sort(false).
KeyBy("Name").
Reduce(func(st []*Student) *Student {
var ret = &Student{
Name: st[0].Name,
Subject: "all",
}
for _, item := range st {
ret.Score = ret.Score + item.Score
}
return ret
}).
Print()
}
缺点:golang有点挫的在于不能在方法里面返回新的泛型类型,比如从student返回一个int类型。虽然能通过在struct定义俩个类型 但是万一要生成第三种类型就无能为力了,不可能一直往后加类型吧(这会导致定义类型超级长 写起来超级丑)。
2.通过函数的方式实现(简单举个例子)
type StreamV2[T any] struct {
data []T
}
func (s StreamV2[T]) Print() {
for i, item := range s.data {
log.Println("idx=", i, " value=", item)
}
}
func FromElementV2[T any](data []T) Stream[T] {
return Stream[T]{
data: data,
}
}
func Map[T any, K any](source Stream[T], mapfunc func(data T) K) StreamV2[K] {
var ret []K
for _, item := range source.data {
ret1 := mapfunc(item)
ret = append(ret, ret1)
}
return StreamV2[K]{
data: ret,
}
}
测试
func TestTostreamv2(t *testing.T) {
stream1 := FromElementV2([]*Student{
&Student{"xyf", "数学", 101},
&Student{"xyf", "语文", 108},
})
stream2 := Map(stream1, func(f *Student) int {
return f.Score
})
stream2.Print()
}
优缺点:这种方式能够将一种容器类型转化为另一种。缺点就是写过java的会吐血(因为搞大数据的朋友都喜欢使用类似builder模式的写法)