AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

Go泛型缺陷?Go Stream是如何解決Go不支持泛型方法的問題的?

來源: 責編: 時間:2023-11-30 09:29:43 251觀看
導讀大家好,我是Coder哥,最近在用Go語言寫項目,也在用泛型解決一些問題,但是也發現了一些問題,今天我們就來聊聊Go語言中泛型函數和泛型方法。起因是這樣的,作為java開發,發現Go沒有類似于java8 stream一樣的流處理框架,導致有些

大家好,我是Coder哥,最近在用Go語言寫項目,也在用泛型解決一些問題,但是也發現了一些問題,今天我們就來聊聊Go語言中泛型函數和泛型方法。tHi28資訊網——每日最新資訊28at.com

起因是這樣的,作為java開發,發現Go沒有類似于java8 stream一樣的流處理框架,導致有些邏輯一行能實現的卻要寫好多行來解決,剛好Go語言也出了泛型,想著用泛型來寫應該能和stream一個效果,于是就有了Go-Stream 這個項目,在寫Go Stream和用的時候發現了一個關于Golang泛型的一個很有意思的問題,想著拿出來聊一下。咱還是循序漸進的展開分析:tHi28資訊網——每日最新資訊28at.com

  1. go-stream框架的簡介
  2. 發現問題的過程。
  3. Go泛型為什么不支持泛型方法?
  4. go-stream框架是怎么解決這個問題的。

go-stream簡介

Go-Stream實現了 java8 stream框架常用的操作,包括 過濾(Filter),轉換一對一(Map), 轉換一對多(FlatMap),轉Map(toMap), 聚合(Reduce),數據統計(Statistic), 分組(GroupingBy)已經分組后對各組排序 等功能,基本滿足99%的開發需求。tHi28資訊網——每日最新資訊28at.com

【Go-Stream】用Go 泛型實現了個 Java-Stream流處理框架tHi28資訊網——每日最新資訊28at.com

Go-stream代碼地址:https://github.com/todocoder/go-streamtHi28資訊網——每日最新資訊28at.com

使用可參閱測試類:https://github.com/todocoder/go-stream/blob/master/stream/stream_test.gotHi28資訊網——每日最新資訊28at.com

require github.com/todocoder/go-stream v1.1.0tHi28資訊網——每日最新資訊28at.com

圖片圖片tHi28資訊網——每日最新資訊28at.com

發現問題

科普一下:tHi28資訊網——每日最新資訊28at.com

方法:是一個代碼塊,由與對象關聯的名稱調用。tHi28資訊網——每日最新資訊28at.com

函數:函數是按名稱調用的代碼,不需要與對象關聯。tHi28資訊網——每日最新資訊28at.com

寫完第一版,基本上能實現一堆花里胡哨的鏈式調用,看起來也很絲滑,比如我想對一個切片做一系列操作,最后得出結果,代碼如下:tHi28資訊網——每日最新資訊28at.com

func TestStream(t *testing.T) {  items := []TestItem{      {itemNum: 7, itemValue: "item7"},{itemNum: 6, itemValue: "item6"},      {itemNum: 1, itemValue: "item1"},{itemNum: 2, itemValue: "item2"},      {itemNum: 3, itemValue: "item3"},{itemNum: 4, itemValue: "item4"},      {itemNum: 5, itemValue: "item5"},{itemNum: 5, itemValue: "item5"},      {itemNum: 5, itemValue: "item5"},{itemNum: 8, itemValue: "item8"},    }    res := Of(items...).Filter(func(item TestItem) bool {      // 過濾掉1的值      return item.itemNum != 4    }).Distinct(func(item TestItem) any {      // 按itemNum 去重      return item.itemNum    }).Sorted(func(a, b TestItem) bool {      // 按itemNum升序排序      return a.itemNum < b.itemNum    }).Skip(1).Limit(6).Reverse().ToSlice()    fmt.Println(res)}
  1. 使用Filter過濾掉1的值
  2. 通過Distinct對itemNum 去重(在第1步的基礎上,下面同理在上一步的基礎上)
  3. 通過Sorted 按itemNum升序排序
  4. 用Skip 從下標為1的元素開始
  5. 使用Limit截取排在前6位的元素
  6. 使用Reverse 對流中元素進行返轉操作
  7. 使用collect終止操作將最終處理后的數據收集到Slice中

看到上面的流程作為一個多年的Javer感覺如此絲滑堪稱完美,輸出的結果也是原來的類型TestItem。tHi28資訊網——每日最新資訊28at.com

但是我們用stream處理問題僅僅是因為一些簡單的單一類型的場景么,那肯定不是了,有人說我想通過這個實現一些類型轉換,或者分組,再對各個組的列表按某個字段排列,比如如下的問題:tHi28資訊網——每日最新資訊28at.com

班級有一組學號{1,2,3,....,12},對應12個人的信息在內存里面存著tHi28資訊網——每日最新資訊28at.com

type Student struct { Num   int Score int Age   int Name  string}studentMap := map[int]Student{  1: {Num: 1, Name: "小明", Score: 3, Age: 26},  2: {Num: 2, Name: "小紅", Score: 4, Age: 27},  3: {Num: 3, Name: "小李", Score: 5, Age: 24},  4: {Num: 4, Name: "老王", Score: 1, Age: 23},  5: {Num: 5, Name: "小王", Score: 2, Age: 24},  6: {Num: 6, Name: "小綠", Score: 2, Age: 24},  7: {Num: 7, Name: "小藍", Score: 3, Age: 29},  8: {Num: 8, Name: "小橙", Score: 3, Age: 30},  9: {Num: 9, Name: "小黃", Score: 4, Age: 29},  10: {Num: 10, Name: "小黑", Score: 5, Age: 15},  11: {Num: 11, Name: "小紫", Score: 3, Age: 15},  12: {Num: 12, Name: "小劉", Score: 2, Age: 15},}

我想把這學號轉換成具體的**Student** 類,然后過濾掉**Score**為 1的,并且再按評分 Score分組,最后對分好后的各組按照Age 降序排列,按最初v1.0.*版本的代碼是這樣的:tHi28資訊網——每日最新資訊28at.com

// v1.0.* 的代碼這樣實現res := Of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12).Map(func(n int) any {  return studentMap[n] }).Filter(func(s any) bool {  // 這里需要強轉  tempS := s.(Student)  // 過濾掉1的  return tempS.Score != 1 }).Collect(collectors.GroupingBy(func(t any) int {  return t.(Student).Score }, func(t any) any {  return t }, func(t1 []any) {  sort.Slice(t1, func(i, j int) bool {   return t1[i].(Student).Age < t1[j].(Student).Age  }) })) println(res)

上面這個代碼有個問題是 經過Map轉換后會丟失類型需要用 any 接收,在用的時候需要強轉成目標類型,并且最后得到res 的結果是 any類型的,用的時候也需要轉換成目標類型,這樣用起來非常麻煩,但是如果按這樣的流式處理,這個問題不能避免。因為官方明確說明,目前Go語言不支持泛型方法tHi28資訊網——每日最新資訊28at.com

如果支持泛型方法,按找目前的編譯機制,可能需要修改編譯器而且會比較復雜tHi28資訊網——每日最新資訊28at.com

為什么Go泛型不好實現泛型方法?

有興趣的可以查看官方說明:https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#no-parameterized-methodstHi28資訊網——每日最新資訊28at.com

如果支持泛型方法,考慮下面一個例子,一共有四個package:tHi28資訊網——每日最新資訊28at.com

package p1// S 是一個普通的struct,但是包含一個泛型方法Identity.type S struct{}// Identity 一個泛型方法,支持任意類型.func (S) Identity[T any](v T) T { return v }
package p2// HasIdentity 定義了一個接口,支持任意實現了泛型方法Identity的類型.type HasIdentity interface { Identity[T any](T) T}
package p3import "p2"http:// CheckIdentity 是一個普通函數,檢查實參是不是實現了HasIdentity接口,如果是,則調用這個接口的泛型方法Identity.func CheckIdentity(v interface{}) { if vi, ok := v.(p2.HasIdentity); ok {  if got := vi.Identity[int](0); got != 0 {   panic(got)  } }}
package p4import ( "p1" "p3")// CheckSIdentity 傳參S給CheckIdentity.func CheckSIdentity() { p3.CheckIdentity(p1.S{})}

作為一個多年用Java的人,一切看起來都沒有問題,但是問題是package p3不知道p1.S類型,整個程序中如果也沒有其它地方調用p1.S.Identity,依照現在的Go編譯器的實現,是沒有辦法為p1.S.Identity[int]生成對應的代碼的。tHi28資訊網——每日最新資訊28at.com

是的,如果go編譯器做的比較復雜,在編譯的時候這個場景是可以識別出來的,但是它需要遍歷整體的程序調用鏈以便生成全部可能的泛型方法,對編譯時間和編譯器復雜性帶來很大的調整。另外一點,如果代碼中通過反射調用的話,編譯器可能會遺漏一些泛型方法的實現,這就很要命了。tHi28資訊網——每日最新資訊28at.com

如果在運行時實現呢?就需要JIT或者反射等技術,這會造成運行時性能的下降。tHi28資訊網——每日最新資訊28at.com

很難實現???如果規定泛型方法不能實現接口呢?那么這類的泛型方法的存在的意義是什么呢?tHi28資訊網——每日最新資訊28at.com

所以目前沒有太好的手段去實現泛型方法,暫時擱置了。tHi28資訊網——每日最新資訊28at.com

期待后面的版本加上。tHi28資訊網——每日最新資訊28at.com

問題是發現,但是要怎么解決這個問題呢,就是我想直接輸出可用的類型,而不是any,因為它用起來實在是太麻煩了tHi28資訊網——每日最新資訊28at.com

go-stream框架是怎么處理這樣的場景的呢

之前用過python 的 groupby 和map, python是這么做的tHi28資訊網——每日最新資訊28at.com

student_group = groupby(stus, key=lambda s: s['score'])

它是把數組作為groupby的方法傳過去,后面是我們的操作,那我們是不是也可以用類似這樣的方式來實現呢?剛好Go語言支持泛型函數,就開搞,于是就有了Go-Stream v1.1.0版了,加了幾個泛型轉換函數,API如下:tHi28資訊網——每日最新資訊28at.com

轉換函數

通過這幾個函數你可以實現類型轉換,分組,flatmap 等處理。tHi28資訊網——每日最新資訊28at.com

注意:這幾個函數非常有用,也是最常用的,由于Go語言泛型的局限性,Go語言方法不支持自己獨立的泛型,所以導致用Stream中的方法轉換只能用 interface{} 代替,這樣會有個非常麻煩的問題就是,轉換后用的時候必須得強轉才能用,所以我把這些寫成轉換函數,就不會受制于類(struct) 的泛型了。tHi28資訊網——每日最新資訊28at.com

APItHi28資訊網——每日最新資訊28at.com

功能說明tHi28資訊網——每日最新資訊28at.com

Map()tHi28資訊網——每日最新資訊28at.com

類型轉換(優點:和上面的Map不一樣的是,這里轉換后可以直接使用,不需要強轉)tHi28資訊網——每日最新資訊28at.com

FlatMap()tHi28資訊網——每日最新資訊28at.com

按照條件將已有元素轉換為另一個對象類型,一對多邏輯,即原來一個元素對象可能會轉換為1個或者多個新類型的元素,返回新的stream流(優點:同Map)tHi28資訊網——每日最新資訊28at.com

GroupingBy()tHi28資訊網——每日最新資訊28at.com

對元素進行逐個遍歷,然后執行給定的處理邏輯tHi28資訊網——每日最新資訊28at.com

Collect()tHi28資訊網——每日最新資訊28at.com

將流轉換為指定的類型,通過collectors.Collector進行指定(優點:轉換后的類型可以直接使用,無需強轉)tHi28資訊網——每日最新資訊28at.com

通過這幾個函數實現上面的分組轉換功能要怎么操作呢?tHi28資訊網——每日最新資訊28at.com

V1.1.0 版本的實現tHi28資訊網——每日最新資訊28at.com

// v1.1.* 的代碼這樣實現res := GroupingBy(Map(Of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), func(n int) Student {  // 注意 這里的返回類型可以是目標類型了  return studentMap[n] }).Filter(func(s Student) bool {  // 這里過濾也不需要轉換類型  // 過濾掉1的  return s.Score != 1 }), func(t Student) int {   // key  return t.Score }, func(t Student) Student {    // v item  return t }, func(t1 []Student) {  // 按年齡降序排列  sort.Slice(t1, func(i, j int) bool {   return t1[i].Age > t1[j].Age  }) }) println(res)

圖片圖片tHi28資訊網——每日最新資訊28at.com

可以看到,中間處理的時候不用轉換,結果也都是強類似的。tHi28資訊網——每日最新資訊28at.com

res 類型:map[int] []Student  返回值的類型我們可以直接用不用轉換。tHi28資訊網——每日最新資訊28at.com

雖然我們不能流式的處理不同的類型,好在用泛型函數也能解決,期待官方后續的版本支持泛型方法,stream處理列表真的非常絲滑,用過的都說好。。哈哈哈。。。tHi28資訊網——每日最新資訊28at.com

最后

作為一個Java開發,用習慣了Stream操作,在網上也沒找到合適的輕量的stream框架,也不知道后續官方是否會出,在這之前,就只能先自己實現了,后面遇到復雜的處理流程會持續的更新到上面除了除了倉庫首頁README里面的功能,還有并行流處理,數據的統計,支持各種分組,轉換等等,有興趣可以自行查看體驗測試類:stream_testtHi28資訊網——每日最新資訊28at.com

有什么問題可以在github上提issues 留言或者公號搜:todocoder,看到后第一時間回復,感謝大家的支持!tHi28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-35320-0.htmlGo泛型缺陷?Go Stream是如何解決Go不支持泛型方法的問題的?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 解密 Python 如何調用 Rust 編譯生成的動態鏈接庫

下一篇: 用200行代碼寫一個H5小游戲

標簽:
  • 熱門焦點
Top