go 进阶 数据处理(内部包含一些针对数据处理的三方库介绍)

一. 复杂结构数据合并

  1. 复杂数据结构根据条件合并需求
  2. 结构体
type OrderInfo struct {
	OrderId         string        `json:"orderId"`
	OccupateionList []Occupateion `json:"occupateionList"`
}

type Occupateion struct {
	RoomNo    string  `json:"roomNo"`
	GuestList []Guest `json:"guestList"`
}

type Guest struct {
	guestId string `json:"guestId"`
	Name    string `json:"name"`
}
  1. 最初的合并示例
func OrderMerge(orderListNew, orderListOld []OrderInfo) []OrderInfo {
	if len(orderListNew) <= 0 {
		return orderListOld
	}
	if len(orderListOld) <= 0 {
		return orderListNew
	}
	var oOld OrderInfo
	for i, oNew := range orderListNew {
		orderFla := true
		for _, oOld = range orderListOld {
			if oNew.OrderId == oOld.OrderId {
				orderFla = false
			}
		}
		if orderFla {
			orderListNew = append(orderListNew, oOld)
			continue
		} else {
			oNew.OccupateionList = OccMerge(oNew.OccupateionList, oOld.OccupateionList)
			orderListNew[i] = oNew
		}
	}
	return orderListNew
}

func OccMerge(occListNew, occListOld []Occupateion) []Occupateion {

	if len(occListNew) <= 0 {
		return occListOld
	}
	if len(occListOld) <= 0 {
		return occListNew
	}
	var occOld Occupateion
	for i, occNew := range occListNew {
		occFlag := true
		for _, occOld = range occListOld {
			if occOld.RoomNo == occNew.RoomNo {
				occFlag = false
			}
		}
		if occFlag {
			occListNew = append(occListNew, occOld)
			continue
		} else {
			occNew.GuestList = GuestMerge(occNew.GuestList, occOld.GuestList)
			occListNew[i] = occNew
		}
	}
	return occListNew
}

func GuestMerge(guestListNew, guestListOld []Guest) []Guest {

	if len(guestListNew) <= 0 {
		return guestListOld
	}
	if len(guestListOld) <= 0 {
		return guestListNew
	}

	for _, gNew := range guestListNew {
		gFlag := true
		for _, gOld := range guestListOld {
			if gNew.guestId == gOld.guestId {
				gFlag = false
			}
		}
		if gFlag {
			guestListOld = append(guestListOld, gNew)
		}
	}
	return guestListOld
}

使用Map优化

func OrderMerge(orderListNew, orderListOld []OrderInfo) []OrderInfo {
	// 创建一个空的 map,用于根据 orderId 存储已有的 orderInfo,以增加查找效率
	orderMap := make(map[string]*OrderInfo, len(orderListOld))
	for i, order := range orderListOld {
		orderMap[order.OrderId] = &orderListOld[i]
	}

	for _, orderNew := range orderListNew {
		if orderOld, ok := orderMap[orderNew.OrderId]; ok {
			// 如果当前 orderId 已存在于 orderListOld 中,则将其 OccupateionList 合并到 orderListOld 中
			orderOld.OccupateionList = OccMerge(orderNew.OccupateionList, orderOld.OccupateionList)
		} else {
			// 如果当前 orderId 不存在于 orderListOld 中,则将整个 orderNew 添加到 orderListOld 中
			orderListOld = append(orderListOld, orderNew)
			//orderNew加入到orderListOld列表后,将orderNew的id标识添加到map中
			orderMap[orderNew.OrderId] = &orderListOld[len(orderListOld)-1]
		}
	}
	return orderListOld
}

func OccMerge(occListNew, occListOld []Occupateion) []Occupateion {
	// 创建一个空的 map,用于根据 roomNo 存储已有的 occupation,以增加查找效率
	occMap := make(map[string]*Occupateion, len(occListOld))
	for i, occ := range occListOld {
		occMap[occ.RoomNo] = &occListOld[i]
	}

	for _, occNew := range occListNew {
		if occOld, ok := occMap[occNew.RoomNo]; ok {
			// 如果当前 roomNo 已存在于 occListOld 中,则将其 GuestList 合并到 occOld 中
			occOld.GuestList = GuestMerge(occNew.GuestList, occOld.GuestList)
		} else {
			// 如果当前 roomNo 不存在于 occListOld 中,则将整个 occNew 添加到 occListOld 中
			occListOld = append(occListOld, occNew)
			occMap[occNew.RoomNo] = &occListOld[len(occListOld)-1]
		}
	}
	return occListOld
}

func GuestMerge(guestListNew, guestListOld []Guest) []Guest {
	// 创建一个空的 map,用于根据 guestId 存储已有的 guest,以增加查找效率
	guestMap := make(map[string]*Guest, len(guestListOld))
	for i, guest := range guestListOld {
		guestMap[guest.guestId] = &guestListOld[i]
	}

	for _, guestNew := range guestListNew {
		if _, ok := guestMap[guestNew.guestId]; !ok {
			// 如果当前 guestId 不存在于 guestListOld 中,则将其添加到 guestListOld 中
			guestListOld = append(guestListOld, guestNew)
			guestMap[guestNew.guestId] = &guestListOld[len(guestListOld)-1]
		}
	}
	return guestListOld
}

使用Visitor访问者设计模式优化

  1. Visitor模式是一种将数据结构和操作解耦的设计模式,将操作封装到一个Visitor对象中,根据具体的需求选择不同的Visitor进行访问,使用Visitor设计模式优化后,代码更加清晰、简洁和可扩展。
  2. 定义OrderVisitor接口,用于访问OrderInfo对象
type OrderVisitor interface {
	VisitOrder(order *OrderInfo)
}

type OrderInfo struct {
	OrderId         string        `json:"orderId"`
	OccupationList  []Occupation  `json:"occupationList"`
}

func (o *OrderInfo) Accept(visitor OrderVisitor) {
	visitor.VisitOrder(o)
}
  1. 定义OccupationVisitor接口,用于访问Occupation对象
type OccupationVisitor interface {
	VisitOccupation(occ *Occupation)
}

type Occupation struct {
	RoomNo    string   `json:"roomNo"`
	GuestList []Guest  `json:"guestList"`
}

func (o *Occupation) Accept(visitor OccupationVisitor) {
	visitor.VisitOccupation(o)
}
  1. 定义三个具体的Visitor:OrderMergeVisitor、OccMergeVisitor和GuestMergeVisitor,分别用于合并OrderInfo对象、Occupation对象和Guest对象:
type OrderMergeVisitor struct {
	orderMap map[string]*OrderInfo
}

func (v *OrderMergeVisitor) VisitOrder(order *OrderInfo) {
	if orderOld := v.orderMap[order.OrderId]; orderOld != nil {
		orderOld.OccupationList = OccMerge(order.OccupationList, orderOld.OccupationList)
	} else {
		v.orderMap[order.OrderId] = order
	}
}

type OccMergeVisitor struct {
	occMap map[string]*Occupation
}

func (v *OccMergeVisitor) VisitOccupation(occ *Occupation) {
	if occOld := v.occMap[occ.RoomNo]; occOld != nil {
		occOld.GuestList = GuestMerge(occ.GuestList, occOld.GuestList)
	} else {
		v.occMap[occ.RoomNo] = occ
	}
}

type GuestMergeVisitor struct {
	guestMap map[string]*Guest
}

func (v *GuestMergeVisitor) VisitGuest(guest *Guest) {
	if _, ok := v.guestMap[guest.GuestId]; !ok {
		v.guestMap[guest.GuestId] = guest
	}
}
  1. 在OrderMerge函数中调用这三个Visitor
func OrderMerge(orderListNew, orderListOld []OrderInfo) []OrderInfo {
	orderMap := make(map[string]*OrderInfo, len(orderListOld))
	for i, order := range orderListOld {
		orderMap[order.OrderId] = &orderListOld[i]
	}

	for _, orderNew := range orderListNew {
		orderNew.Accept(&OrderMergeVisitor{orderMap})
	}

	returnMap := make(map[string]*OrderInfo, len(orderListOld))
	for _, order := range orderMap {
		order.Accept(&OccMergeVisitor{returnMap})
	}
	
	var resultList []OrderInfo
	for _, order := range returnMap {
		order.Accept(&GuestMergeVisitor{returnMap})
		resultList = append(resultList, *order)
	}

	return resultList
}

二. Go中的FunctionalOptions可选参数模式

  1. Functional Options 模式的优点:
  1. 易于阅读和维护: 使用 Functional Options 模式能够使代码可读性更强,易于维护。对于一些带有很多可选参数的函数,如果不使用 Functional Options 模式,那么在调用这个函数时需要填写很多参数,而且如果忘了填写某个参数,编译器也不会给出警告或者错误,可能会导致一些问题,使用 Functional Options 模式则可以避免这种情况。
  2. 自由组合: 使用 Functional Options 模式能够让用户根据自己的需求任意组合选项参数,从而提高函数的灵活性。同样的函数,不同的选项参数可以产生不同的效果,用户可以根据自己的需求来自由组合选项参数,从而实现不同的功能。
  1. 示例1
type Option func([]*OrderInfo) []*OrderInfo

//根据日期过滤订单
func FilterOrdersByDate(start, end time.Time) Option {
  return func(orders []*OrderInfo) []*OrderInfo {
    filteredOrders := []*OrderInfo{}
    for _, order := range orders {
      if order.Date.After(start) && order.Date.Before(end) {
        filteredOrders = append(filteredOrders, order)
      }
    }
    return filteredOrders
  }
}

//按照日期降序排序订单
func SortOrdersByDateDesc() Option {
  return func(orders []*OrderInfo) []*OrderInfo {
    sort.Slice(orders, func(i, j int) bool {
      return orders[i].Date.After(orders[j].Date)
    })
    return orders
  }
}

//订单列表和任意数量的选项函数作为参数,通过遍历选项函数来对订单列表进行流式处理
func ProcessOrders(orders []*OrderInfo, options ...Option) []*OrderInfo {
  for _, option := range options {
    orders = option(orders)
  }
  return orders
}
  1. 示例2
//1.定义 Options 结构体
type Options struct {
    title       string
    description string
    keywords    []string
    rewrites    map[string]string
}

//2.实现Option接口
type Option func(opt *Options)

//3.定义不同的Option函数实现
func Title(title string) Option {
    return func(opt *Options) {
        opt.title = title
    }
}

func Description(description string) Option {
    return func(opt *Options) {
        opt.description = description
    }
}

func Keywords(keywords []string) Option {
    return func(opt *Options) {
        opt.keywords = keywords
    }
}

func Rewrites(rewrites map[string]string) Option {
    return func(opt *Options) {
        opt.rewrites = rewrites
    }
}

type Page struct {
    title       string
    description string
    keywords    []string
    rewrites    map[string]string
}

//接收可选参数,遍历执行
func NewPage(url string, title string, options ...Option) *Page {
    opt := &Options{}
    for _, o := range options {
        o(opt)
    }

    page := &Page{
        title:       opt.title,
        description: opt.description,
        keywords:    opt.keywords,
        rewrites:    opt.rewrites,
    }
    return page
}

//运行示例
page := NewPage("https://www.example.com/", "Example", Title("Example Page"), Description("This is an example page."))

三. Go中的Pipeline模式

  1. 实现流式处理的一种常见方式,主要通过将数据处理过程分解成多个阶段,并将每个阶段封装成一个函数,然后通过将这些函数串联起来形成一个管道,从而实现流式处理
  1. 易于编写和组合: Pipeline 模式将数据处理过程划分成多个阶段,每个阶段只需要关注自己的输入和输出,这大大降低了代码的复杂度和耦合度,同时使得编写和组合阶段函数变得非常容易。
  2. 可重用性强: Pipeline 模式中的每个阶段函数都是独立的,可以在不同的 Pipeline 中重复使用,提高代码的可重用性。
  3. 可扩展性强: Pipeline 模式中的每个阶段函数都可以很容易地被替换或者新增,使得整个 Pipeline 的功能得到扩展或改进
  1. 示例
//根据日期过滤订单
func FilterOrdersByDate(orders []*OrderInfo, start, end time.Time) []*OrderInfo {
  filteredOrders := []*OrderInfo{}
  for _, order := range orders {
    if order.Date.After(start) && order.Date.Before(end) {
      filteredOrders = append(filteredOrders, order)
    }
  }
  return filteredOrders
}

//按照日期降序排序订
func SortOrdersByDateDesc(orders []*OrderInfo) []*OrderInfo {
  sort.Slice(orders, func(i, j int) bool {
    return orders[i].Date.After(orders[j].Date)
  })
  return orders
}

//ProcessOrders 函数接受一个订单列表和多个阶段函数作为参数,通过遍历阶段函数来对订单列表进行流式处理
func ProcessOrders(orders []*OrderInfo, pipeline ...func([]*OrderInfo) []*OrderInfo) []*OrderInfo {
  for _, stage := range pipeline {
    orders = stage(orders)
  }
  return orders
}
  1. 示例2
type Student struct {
    Name   string
    Scores []int
}

func FilterTop10Students(students []*Student) []*Student {
    top10Students := make([]*Student, 0, 10)
    for _, s := range students {
        if len(top10Students) < 10 {
            top10Students = append(top10Students, s)
        } else {
            minScore := top10Students[0].Scores[0]
            minIndex := 0
            for i, t := range top10Students {
                if t.Scores[0] < minScore {
                    minScore = t.Scores[0]
                    minIndex = i
                }
            }
            if s.Scores[0] > minScore {
                top10Students[minIndex] = s
            }
        }
    }
    return top10Students
}

func SortStudentsByAverageScore(students []*Student) []*Student {
    sort.Slice(students, func(i, j int) bool {
        sumI := 0
        for _, score := range students[i].Scores {
            sumI += score
        }
        avgI := float64(sumI) / float64(len(students[i].Scores))

        sumJ := 0
        for _, score := range students[j].Scores {
            sumJ += score
        }
        avgJ := float64(sumJ) / float64(len(students[j].Scores))

        return avgI > avgJ
    })
    return students
}

func PrintStudents(students []*Student) {
    fmt.Println("排名\t姓名\t平均分")
    for i, s := range students {
        sum := 0
        for _, score := range s.Scores {
            sum += score
        }
        avg := float64(sum) / float64(len(s.Scores))
        fmt.Printf("%d\t%s\t%.2f\n", i+1, s.Name, avg)
    }
}

func main() {
    students := []*Student{
        {"张三", []int{67, 82, 76}},
        {"李四", []int{87, 65, 94}},
        {"王五", []int{69, 74, 79}},
        {"赵六", []int{91, 88, 87}},
        {"钱七", []int{82, 73, 64}},
        {"韩八", []int{94, 96, 92}},
    }

    top10Students := FilterTop10Students(students)
    sortedStudents := SortStudentsByAverageScore(top10Students)
    PrintStudents(sortedStudents)
}

四. Go中的MapReduce模式

  1. MapReduce 模式优点:是一种分布式计算模型,主要用于处理大规模数据集。该模型包含两个阶段:Map 阶段和 Reduce 阶段。在 Map 阶段中,数据会被分割成多个小块,并且每个小块都会被映射为一个键值对;在 Reduce 阶段中,则将所有具有相同键的值进行合并,并最终输出结果
  1. 可以处理大规模数据集,支持分布式计算。
  2. 支持数据并行处理,提高了计算效率。
  3. 相比于传统的单线程或者多线程计算模型,MapReduce 模式具有更好的容错和故障处理能力
  1. 示例
package main

import (
	"fmt"
	"sort"
)

func main() {
	text := "afs dfasgah afdgfad gasd dffsdfas"

	// Map 阶段:
	//将输入数据分割成多个块,并且将每个小块映射为一个键值对
	//Key: 表示词语, Value: 表示该词语在文本中出现的次数
	var keyValueMaps []map[string]int
	words := splitWords(text)
	for _, word := range words {
		keyValueMap := map[string]int{word: 1}
		keyValueMaps = append(keyValueMaps, keyValueMap)
	}

	// Shuffle 阶段:
	//根据 Key 值对中间结果进行重新分组,将拥有相同 Key 值的 Value 放到一起
	intermediate := make(map[string][]int)
	for _, keyValueMap := range keyValueMaps {
		for key, value := range keyValueMap {
			intermediate[key] = append(intermediate[key], value)
		}
	}

	// Reduce 阶段:
	//对每个中间结果进行归并操作,这里的中间结果就是某个词语出现次数的列表
	var keys []string
	for k := range intermediate {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	finalResult := make(map[string]int)
	for _, key := range keys {
		finalResult[key] = reduce(key, intermediate[key])
	}

	// 输出结果
	fmt.Println(finalResult)
}

// reduce 函数用于对每个中间结果进行归并操作,
//这里的中间结果就是某个词语出现次数的列表
func reduce(key string, values []int) int {
	sum := 0
	for _, v := range values {
		sum += v
	}
	return sum
}

// map 函数用于对输入的数据进行分割和映射操作,
//这里的输入数据就是一段文本字符串
func splitWords(input string) []string {
	var words []string
	word := ""
	for i := 0; i < len(input); i++ {
		if input[i] != ' ' {
			word += string(input[i])
		} else {
			words = append(words, word)
			word = ""
		}
	}
	if word != "" {
		words = append(words, word)
	}
	return words
}

// map 函数用于将每个词语映射为一个键值对,
//Key 表示词语,Value 表示该词语在文本中出现的次数
func mapToKeyValue(words []string) map[string]int {
	keyValueMap := make(map[string]int)
	for _, word := range words {
		if _, ok := keyValueMap[word]; ok {
			keyValueMap[word] += 1
		} else {
			keyValueMap[word] = 1
		}
	}
	return keyValueMap
}

五. 三方库 go-linq

  1. go-linq是一个开源的Go语言库,提供了基于LINQ风格的查询语法,支持对切片、字典、数组等数据进行查询、过滤、排序、分组等操作,并且代码简洁清晰易懂,使用方便
  2. go-linq常用API介绍
//从输入的切片、字典、数组、通道或迭代器创建一个Enumerable对象
func From(s interface{}) Enumerable
//根据给定的条件筛选元素,并返回一个新的Enumerable对象
func (e Enumerable) Where(f func(interface{}) bool) Enumerable

//将输入的元素通过给定的转换函数进行转换,并返回一个新的Enumerable对象
func (e Enumerable) Select(f func(interface{}) interface{}) Enumerable

//在两个Enumerable对象之间建立内部联接,并返回一个新的Enumerable对象
func (e Enumerable) Join(inner Enumerable, outerKeySelector func(interface{}) interface{}, innerKeySelector func(interface{}) interface{}, resultSelector func(interface {}, interface {}) interface{}) Enumerable

//根据给定的键选择器对元素进行分组,并返回一个新的Enumerable对象
func (e Enumerable) GroupBy(keySelector func(interface{}) interface{}, elementSelector func(interface{}) interface{}) Enumerable

//根据给定的键选择器对元素进行升序排序,并返回一个新的Enumerable对象
func (e Enumerable) OrderBy(fn func(interface{}) interface{}) Enumerable

//根据给定的键选择器对元素进行降序排序,并返回一个新的Enumerable对象
func (e Enumerable) OrderByDescending(fn func(interface{}) interface{}) Enumerable

//在OrderBy操作之后,并根据给定的键选择器进行升序排序,并返回一个新的Enumerable对象
func (e Enumerable) ThenBy(fn func(interface{}) interface{}) Enumerable

//在OrderBy操作之后,并根据给定的键选择器进行降序排序,并返回一个新的Enumerable对象
func (e Enumerable) ThenByDescending(fn func(interface{}) interface{}) Enumerable

//将Enumerable对象转换为目标类型,并存储到指定的切片中
func (e Enumerable) ToSlice(target interface{})

//根据给定的键选择器和元素选择器构建一个字典,并返回一个map对象
func (e Enumerable) ToMap(keySelector func(interface{}) interface{}, elementSelector func(interface{}) interface{}) map[interface{}]interface{}

//计算Enumerable对象中所有元素的总和,并返回一个浮点型数值
func (e Enumerable) Sum() (sum float64)

//计算Enumerable对象中所有元素的平均值,并返回一个浮点型数值
func (e Enumerable) Average() (average float64)

//查找Enumerable对象中最小的元素,并返回一个接口类型的数值
func (e Enumerable) Min() (min interface{})

//查找Enumerable对象中最大的元素,并返回一个接口类型的数值
func (e Enumerable) Max() (max interface{})

//计算Enumerable对象中元素的数量,并返回一个整型数值
func (e Enumerable) Count() (count int)

//查找Enumerable对象中第一个元素,并返回一个接口类型的数值
func (e Enumerable) First() interface{}

//查找Enumerable对象中最后一个元素,并返回一个接口类型的数值
func (e Enumerable) Last() interface{}

//查找Enumerable对象中单一的元素,并返回一个接口类型的数值
func (e Enumerable) Single() interface{}
  1. 使用示例
package main

import (
	"fmt"
	"github.com/ahmetb/go-linq"
)

type Student struct {
	Name string
	Age  int
}

func main() {
	students := []Student{
		{Name: "Tom", Age: 18},
		{Name: "Mary", Age: 21},
		{Name: "Jack", Age: 25},
		{Name: "Lucy", Age: 19},
		{Name: "Bob", Age: 22},
	}

	// 筛选和排序
	result := make([]Student, 0)
	linq.From(students).Where(func(s interface{}) bool {
		return s.(Student).Age > 20
	}).OrderBy(func(s interface{}) interface{} {
		return s.(Student).Name
	}).ToSlice(&result)

	// 输出结果
	fmt.Println("Filtered and sorted students:")
	for _, student := range result {
		fmt.Printf("%s (%d)\n", student.Name, student.Age)
	}
}

六. 三方库 go-streams

  1. go-streams是一个基于Go语言的功能强大的流处理框架,能够让用户方便地进行流式数据处理,包括数据转换、聚合、筛选过滤等操作。它提供了一系列高层次的API,使得用户可以轻松地创建和操作流数据,同时还支持分布式处理和无状态处理等功能
  2. go-streams中常用的API如下:
//创建一个新的管道,接收一个或多个操作
streams.NewPipeline(...streams.Operation)

//创建一个数据源,接受一个切片类型的参数
streams.NewSliceSource([]interface{})

//对数据进行转换,接受一个函数类型的参数,该函数将一个输入元素转换为另一个输出元素
streams.Map(func(interface{}) interface{})

//对数据进行筛选过滤,接受一个函数类型的参数,该函数返回一个布尔值,用于标识该元素是否符合筛选条件
streams.Filter(func(interface{}) bool)

//对数据进行扁平化处理,接收一个函数类型的参数,该函数将一个输入元素转换为多个输出元素
streams.FlatMap(func(interface{}) []interface{})

//对数据进行遍历,接受一个函数类型的参数,该函数对每个元素进行操作
streams.ForEach(func(interface{}))

//对数据进行聚合操作,接受一个函数类型的参数,该函数将两个参数聚合为一个
streams.Reduce(func(interface{}, interface{}) interface{})

//对数据进行分组操作,接受一个函数类型的参数,该函数将一个输入元素映射为一个分组键
streams.GroupByKey(func(interface{}) interface{})

//以指定方式分组,并对每个分组执行指定的操作
streams.GroupBy(func(interface{}) interface{}, ...streams.Operation)

//将数据按照指定的窗口大小进行分组,接受一个WindowConfig类型的参数,该参数包含了窗口大小和滑动间隔等信息
streams.Window(streams.WindowConfig)

//执行管道操作,将数据源作为参数传入,并返回处理结果
Execute(streams.Source) streams.Result
  1. go-streams应用案例
// 定义一个数据源
source := streams.NewSliceSource([]map[string]interface{}{
    {"name": "Alice", "age": 18},
    {"name": "Bob", "age": 22},
    {"name": "Cathy", "age": 26},
})

// 定义管道中的处理步骤
pipeline := streams.NewPipeline(
    // 将map类型转换为Person类型
    streams.Map(func(item interface{}) interface{} {
        m := item.(map[string]interface{})
        return Person{
            Name: m["name"].(string),
            Age:  m["age"].(int),
        }
    }),
    // 筛选年龄大于20岁的人员信息
    streams.Filter(func(item interface{}) bool {
        return item.(Person).Age > 20
    }),
    // 将Person类型转换为XML格式
    streams.Map(func(item interface{}) interface{} {
        p := item.(Person)
        return fmt.Sprintf("<person><name>%s</name><age>%d</age></person>", p.Name, p.Age)
    }),
)

// 执行管道操作
result := pipeline.Execute(source)

// 输出处理结果
fmt.Println(result.Get())

七. 三方库 streams

  1. streams库(https://github.com/razonyang/streams),一个用于处理数据流的第三方库。该库提供了类似Java 8 Stream API和.NET LINQ的API,支持各种常见的数据操作,如Map、Filter、Reduce等,还提供了一些其他的功能和扩展,包括:
  1. 迭代器(Iterator):提供了对数据源进行迭代遍历的功能
  2. 并行计算(Parallel computing):提供了对数据流进行并行处理的支持,可以充分利用多核CPU的优势
  3. IO操作(IO operations):提供了对文件、网络等IO操作的支持,可以方便地进行读写操作
  4. 数据库操作(Database operations):提供了对关系型数据库的支持,可以使用SQL语句进行查询、插入、更新、删除
  5. HTTP客户端(HTTP client):提供了对HTTP协议的客户端支持,可以方便地进行GET、POST、PUT、DELETE等
  1. streams一下常用API
//1.Stream下:/表示一个流对象,它包含了所有的流处理操作
//创建一个新的流对象,将输入的数据源包装成一个流对象,以便进行后续的处理
func New(source interface{}) *Stream

func of()

//将每个元素转换为切片类型,并将所有切片合并为一个新的流。
func (s *Stream) FlatMap(f interface{}) *Stream

//对每个元素应用指定的转换函数,将原始流转换为另一个流
func (s *Stream) Map(mapper interface{}) *Stream

//对每个元素应用指定的转换函数,扁平化元素集合并创建一个新的流
func (s *Stream) FlatMap(mapper interface{}) *Stream

//过滤出符合条件的元素,创建一个新的流
func (s *Stream) Filter(predicate interface{}) *Stream

//去重,创建一个新的流
func (s *Stream) Distinct() *Stream

//跳过前n个元素,创建一个新的流
func (s *Stream) Skip(n int) *Stream

//只保留前n个元素,创建一个新的流
func (s *Stream) Limit(n int) *Stream

//使用指定的比较器对元素进行排序,创建一个新的流
func (s *Stream) Sorted(comparator interface{}) *Stream

//对每个元素执行指定的操作,并将元素传递到下一个流中
func (s *Stream) Peek(action interface{}) *Stream

//对每个元素执行指定的操作
func (s *Stream) ForEach(action interface{})

//使用指定的累加器函数对所有元素进行计算,返回一个最终的结果
func (s *Stream) Reduce(identity, accumulator interface{}) interface{}

//使用指定的收集器函数将所有元素聚合成一个单一的对象,并返回该对象
func (s *Stream) Collect(collector interface{}) interface{}

//按照指定的键值进行分组,返回一个包含所有分组信息的map对象
func (s *Stream) GroupBy(keyMapper interface{}) map[interface{}]Group

//将两个流中的元素进行合并,并返回一个新的流
func (s *Stream) Join(other *Stream, leftKeyMapper, rightKeyMapper, resultMapper interface{}) *Stream

//将流中的所有元素转换成一个切片类型并返回
func (s *Stream) AsSlice() []interface{}


//2.Group 表示一个分组对象,它包含了所有分组的元素及其键值:
//返回该分组的键值
func (g Group) Key() interface{}

//返回该分组的所有元素所在的流对象
func (g Group) Stream() *Stream


//3.ParallelStream 表示一个并行流对象,它包含了所有的并行流操作:
//对每个元素应用指定的转换函数,将原始并行流转换为另一个并行流
func (ps *ParallelStream) Map(mapper interface{}) *ParallelStream

//对每个元素应用指定的转换函数,扁平化元素集合并创建一个新的并行流
func (ps *ParallelStream) FlatMap(mapper interface{}) *ParallelStream

//过滤出符合条件的元素,创建一个新的并行流
func (ps *ParallelStream) Filter(predicate interface{}) *ParallelStream

//去重,创建一个新的并行流
func (ps *ParallelStream) Distinct() *ParallelStream

//跳过前n个元素,创建一个新的并行流
func (ps *ParallelStream) Skip(n int) *ParallelStream

//只保留前n个元素,创建一个新的并行流
func (ps *ParallelStream) Limit(n int) *ParallelStream

//使用指定的比较器对元素进行排序,创建一个新的并行流
func (ps *ParallelStream) Sorted(comparator interface{}) *ParallelStream

//对每个元素执行指定的操作,并将元素传递到下一个并行流中
func (ps *ParallelStream) Peek(action interface{}) *ParallelStream

//对每个元素执行指定的操作
func (ps *ParallelStream) ForEach(action interface{})

//使用指定的累加器函数对所有元素进行计算,返回一个最终的结果
func (ps *ParallelStream) Reduce(identity, accumulator interface{}) interface{}

//使用指定的收集器函数将所有元素聚合成一个单一的对象,并返回该对象
func (ps *ParallelStream) Collect(collector interface{}) interface{}

//按照指定的键值进行分组,返回一个包含所有分组信息的map对象
func (ps *ParallelStream) GroupBy(keyMapper interface{}) map[interface{}]ParallelGroup

//将两个并行流中的元素进行合并,并返回一个新的并行流
func (ps *ParallelStream) Join(other *ParallelStream, leftKeyMapper, rightKeyMapper, resultMapper interface{}) *ParallelStream


//4.ParallelGroup 表示一个分组对象,它包含了所有分组的元素及其键值:
//返回该分组的键值
func (pg ParallelGroup) Key() interface{}

//返回该分组的所有元素所在的并行流对象
func (pg ParallelGroup) Stream() *ParallelStream

八. 三方库 optional

  1. optional(https://github.com/markphelps/optional)是 Go 语言的第三方库,提供了一种简洁而方便的方法来处理可选值,尤其是在处理可能为空的数据时非常有用
  2. optional 库的核心是 Optional 类型,表示一个可选值。Optional 对象包含一个内部值value,以及一个标志位present,用于表示该值是否存在。当 present 为 false 时,value 可以为任何类型的零值,例如 nil、false、0 等,表示该可选值不存在;当 present 为 true 时,value 则表示实际的值
  3. 常用API总结
//接收一个值value并返回一个对应类型T的新可选值
//如果传入的值为 nil,则 present 属性被设置为 false,value 属性被设置为对应类型的默认值,
//否则 present 属性被设置为 true,value 属性被设置为传入的值。
NewT(value T) Optional

//返回该可选值是否存在,即 present 属性的值
Present() bool

//返回该可选值的实际值(如果存在),否则会引发 panic
Get() T

//返回该可选值的实际值(如果存在),否则返回指定的默认值
OrElse(defaultValue T) T

//接收一个函数 f 并返回一个新的可选值,该可选值包含了把当前可选值的实际值应用到函数 f 上得到的结果。
//如果当前可选值不存在,则返回一个空可选值。
Map(f func(T) R) Optional[R]

//接收一个函数 predicate 并返回一个新的可选值,
//该可选值包含了把当前可选值的实际值应用到函数 predicate 上得到的结果。
//如果当前可选值不存在或者返回值为 false,则返回一个空可选值。
Filter(predicate func(T) bool) Optional

//返回该可选值的实际值(如果存在),否则返回由提供者函数 supplier 创建的默认值。
OrElseGet(supplier func() T) T

//接收一个函数 consumer,如果该可选值存在,则将其实际值应用到这个函数上。
IfPresent(consumer func(T))
  1. 使用示例
import (
    "fmt"
    "github.com/markphelps/optional"
)

func GetString() optional.String {
    // 模拟从 API 或数据库中获取字符串的过程
    var str string
    if ... { // 判断获取字符串是否成功
        return optional.NewString(str)
    } else {
        return optional.String{}
    }
}

func main() {
    // 获取一个可能为空的字符串
    value := GetString()

    // 如果值存在,则将其转为大写并输出
    uppercase := value.Map(strings.ToUpper)
    if uppercase.Present() {
        fmt.Println(uppercase.Get())
    } else {
        fmt.Println("String is absent")
    }

    // 如果值不存在,则返回一个默认值
    defaultVal := value.OrElse("default")
    fmt.Println(defaultVal)

    // 如果值存在且符合条件,则返回一个新的可选值
    filtered := value.Filter(func(s string) bool {
        return len(s) > 5
    })
    if filtered.Present() {
        fmt.Println(filtered.Get())
    } else {
        fmt.Println("String is absent or too short")
    }
}

九. 三方库 maybe

  1. maybe(https://github.com/xdg/scramjet),个提供了Maybe类型的库,支持类似于Scala中的集合Option类型的处理方式,能够更好地处理缺失值和错误信息等情况
  2. Maybe 库的核心是 Maybe 类型,它表示一个可选值。Maybe 对象包含一个内部值value,以及一个标志位ok,用于表示该值是否存在。当 ok 为 false 时,value 可以为任何类型的零值,例如 nil、false、0 等,表示该可选值不存在;当 ok 为 true 时,value 则表示实际的值。
  3. 常用API总结
//接收一个值value并返回一个新的可选值。
//如果传入的值为 nil 或者该值类型是一个结构体类型的零值,则 ok 属性被设置为 false,
//value 属性被设置为对应类型的默认值,
//否则 ok 属性被设置为 true,value 属性被设置为传入的值
New(value interface{}) Maybe

//接收一个值value并返回一个值存在的可选值。如果传入的值为 nil,则会引发 panic
Just(value interface{}) Maybe

//返回一个值不存在的可选值
Nothing() Maybe

//返回该可选值是否为空,即 ok 属性的值
IsNothing() bool

//返回该可选值的实际值(如果存在),否则会引发 panic
Unwrap() interface{}

//返回该可选值的实际值(如果存在),否则返回指定的默认值
UnwrapOr(def interface{}) interface{}

//接收一个函数 f 并返回一个新的可选值,
//该可选值包含了把当前可选值的实际值应用到函数 f 上得到的结果
//如果当前可选值不存在,则返回一个空可选值
Map(f func(interface{}) interface{}) Maybe

//返回该可选值的实际值(如果存在),否则返回指定的默认值的可选值
Default(def interface{}) Maybe

//接收一个函数 f 并返回一个新的可选值,
//该可选值包含了把当前可选值的实际值应用到函数 f 上得到的结果
//如果当前可选值不存在或者返回值为 false,则返回一个空可选值
Filter(f func(interface{}) bool) Maybe
  1. 使用示例
package main

import (
	"fmt"
	"github.com/xdg/scramjet/maybe"
)

func main() {
	// 创建一个值存在的可选值
	optionalValue := maybe.Just("Hello, Maybe!")
	fmt.Printf("The optional value is: %v\n", optionalValue)

	// 创建一个值不存在的可选值
	emptyValue := maybe.Nothing()
	fmt.Printf("The empty value is: %v\n", emptyValue)

	// 获取可选值的实际值
	value := optionalValue.Unwrap()
	fmt.Printf("The value is: %v\n", value)

	// 判断可选值是否为空
	isEmpty := emptyValue.IsNothing()
	fmt.Printf("The empty value is nothing: %v\n", isEmpty)

	// 将可选值转换为大写字母
	upperValue := optionalValue.Map(func(v interface{}) interface{} {
		s, ok := v.(string)
		if !ok {
			return ""
		}
		return strings.ToUpper(s)
	})
	fmt.Printf("The upper value is: %v\n", upperValue)

	// 使用默认值
	defaultValue := emptyValue.Default("default")
	fmt.Printf("The default value is: %v\n", defaultValue)

	// 过滤可选值
	filteredValue := optionalValue.Filter(func(v interface{}) bool {
		s, ok := v.(string)
		if !ok {
			return false
		}
		return len(s) > 5
	})
	fmt.Printf("The filtered value is: %v\n", filteredValue)
}

十. 针对上方数据处理三方库总结

  1. 市场使用角度:
  1. 目前来看,使用较广的库是 go-linq,根据 Github 上的数据,它有超过 4K 的 star 和大量的 fork,可以说是一个比较成熟和受欢迎的库
  2. Golang-collections 提供了几种常见的数据结构,例如 Set、Deque、Queue 和 Stack,并且支持Filter、Map、Reduce、GroupBy、OrderBy 等,这些函数都是支持链式调用https://github.com/golang-collections/collections,并且有 2.9K 的 stars
  3. 虽然 go-streams 和 streams 也提供了流式处理的功能,但是它们的 star 数相对较少。在选择使用库的时候,除了考虑其功能是否满足需求之外,还需要考虑其文档资源、社区支持和开发者活跃度等因素
  1. go-linq 优缺点:
  1. 优缺: 功能丰富,提供了丰富的 LINQ 风格的函数操作。底层采用指针和 Unsafe 等技术,性能较好。
  2. 缺点: 不支持链式操作。由于采用了 Unsafe 技术,存在一定的安全隐患。
  1. go-streams 优缺点:
  1. 优点: 支持多种数据源,包括数组、切片、通道、文件等。支持链式操作,支持并行处理,可以运行在多个 CPU 上。
  2. 缺点: 依赖性较高,需要引入第三方库。函数操作相对较少,不如 go-linq 和 Golang-collections 丰富。
  1. streams 优缺点:
  1. 优点: 支持链式操作,具有丰富的操作符,包括 Map、Filter、Reduce 等。
  2. 缺点:功能相对较少不如 go-linq 和 Golang-collections 丰富。底层实现性能可能略逊于 go-linq 和 Golang-collections。
  1. Golang-collections优缺点:
  1. 优点: 提供了丰富的数据结构和集合操作函数,功能比较丰富。性能较好,底层采用了指针和 Unsafe 等技术。
  2. 缺点: 不支持链式操作,功能相对于 go-linq 和 go-streams 稍显简单。
  1. 综上所述,选择哪个三方库取决于具体需求。如果需要 LINQ 风格的操作,可以选择 go-linq;如果需要多种数据源和支持并行处理,可以选择 go-streams;如果需要丰富的数据结构和集合操作函数,可以选择 Golang-collections;而如果需要链式操作,可以选择 streams。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值