Go語言實現的Java Stream API
學習Go語言時實現的集合操作工具庫,類似於Java 8 中新增的Stream API。由於Go語言不支援泛型,所以基於反射實現。只用於學習目的,不要用於生產(PS:當然也不會有人用)。
專案地址:https://github.com/tk103331/stream
集合操作包括生成操作、中間操作和終止操作。 生成操作返回值是Steam物件,相當於資料的源頭,可以呼叫Stream的其他方法;中間操作返回值是Stream物件,可以繼續呼叫Stream的方法,即可以鏈式呼叫方法;終止操作不能繼續呼叫方法。
下面介紹下這個庫的API:
資料準備 後面的操作都是基於集合資料的,先準備一些測試資料。
type student struct {
id int
name string
ageint
scores []int
}
func (s *student) String() string {
return fmt.Sprintf("{id:%d, name:%s, age:%d,scores:%v}", s.id, s.name, s.age, s.scores)
}
func createStudents() []student {
names := []string{"Tom", "Kate", "Lucy", "Jim", "Jack", "King", "Lee", "Mask"}
students := make([]student, 10)
rnd := func(start, end int) int { return rand.Intn(end-start) + start }
for i := 0; i < 10; i++ {
students[i] = student{
id: i + 1,
name: names[rand.Intn(len(names))],
age:rnd(15, 26),
scores: []int{rnd(60, 100), rnd(60, 100), rnd(60, 100)},
}
}
return students
}
type node struct {
id int
next *node
}
func createNodes() *node {
i := 10
n := &node{id: i}
for i > 0 {
i--
n = &node{id: i, next: n}
}
return n
}
迴圈遍歷 ForEach
迴圈遍歷集合中的每一個元素,需要提供一個包含一個引數的處理函式作為引數,形如 func(o T),迴圈遍歷時會把每個元素作為處理函式的實參。 ForEach 方法是終止操作。
func (s *stream) ForEach(actFunc interface{})
例子:
students := createStudents()
stream, _ := New(students)
stream.ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})
輸出:
{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:3, name:Lee, age:15,scores:[62 69 68]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:5, name:Mask, age:15,scores:[68 78 67]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:9, name:King, age:21,scores:[94 63 93]}
{id:10, name:Jim, age:20,scores:[64 99 93]}
迭代器 Iterate
It 方法可以從一個迭代器中建立一個Stream物件,迭代器就是一個迭代產生資料的迭代函式,迭代函式形如 func(prev T) (next T,more bool),迭代函式的引數為上一個元素的值,返回值是下一個元素的值,和是否還有更多元素。 It 方法是生成操作。
func It(initValue interface{}, itFunc interface{}) (*stream, error)
Sample:
stream, _ := It(root, func(n *node) (*node, bool) {
return n.next, n.next.next != nil
})
stream.ForEach(func(n *node) {
fmt.Printf("\tnode{id:%d}\n", n.id)
})
Output:
node{id:1}
node{id:2}
node{id:3}
node{id:4}
node{id:5}
node{id:6}
node{id:7}
node{id:8}
node{id:9}
node{id:10}
生成器 Generate
Gen 方法可以從一個生成器中建立一個Stream物件,生成器就是一個不斷產生資料的生成函式,生成函式形如 func() (next T,more bool),生成函式沒有引數,返回值是下一個元素的值,和是否還有更多元素。 Gen 方法是生成操作。 Gen 方法和It 方法的區別就是,它可以不依賴上一個元素的值。
func Gen(genFunc interface{}) (*stream, error)
例子:
stream, _ := Gen(func() (int, bool) {
x := rand.Intn(10)
return x, x < 8
})
stream.ForEach(func(x int) {
fmt.Printf("\t%d\n", x)
})
輸出:
1
7
7
9
過濾 Filter
Filter 方法對集合中的元素進行過濾,篩選出符合條件的元素,需要提供一個過濾函式,過濾函式形如func(o T) bool,引數為集合中的元素,返回值是表示該元素是否符合條件。 Filter 方法是中間操作。
func (s *stream) Filter(filterFunc interface{}) *stream
例子:
students := createStudents()
stream, _ := New(students)
stream.Filter(func(s student) bool {
return s.age > 20
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})
輸出:
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:9, name:King, age:21,scores:[94 63 93]}
對映 Map
Map 方法可以將集合中的每個元素對映為新的值,從而得到一個新的集合,需要提供一個對映函式,形如func(o T1) T2,引數為集合中的元素,返回值是表示該元素對映的新值。 Map 方法是中間操作。
func (s *stream) Map(mapFunc interface{}) *stream
例子:
students := createStudents()
stream, _ := New(students)
stream.Map(func(s student) string {
return s.name
}).ForEach(func(s string) {
fmt.Printf("\t%s\n", s)
})
輸出:
Kate
Lee
Lee
Lucy
Mask
Jim
King
Jack
King
Jim
打平對映 FlatMap
FlatMap 方法可以將集合中每個元素對映為多個元素,返回新的集合包含對映的所有元素。需要提供一個對映函式,形如 func(o T1) []T2,引數為集合中的元素,返回值是表示該元素對映的新值的集合。 FlatMap 方法是中間操作。 FlatMap 方法和Map 方法的區別在於,它可以將集合中每個元素巢狀的集合打平,合併為新的集合。
func (s *stream) FlatMap(mapFunc interface{}) *stream
例子:
students := createStudents()
stream, _ := New(students)
var data []int
stream.FlatMap(func(s student) []int {
return s.scores
}).ToSlice(&data)
fmt.Printf("\t%v\n", data)
輸出:
[67 79 61 80 76 80 62 69 68 65 97 86 68 78 67 68 90 75 87 91 89 91 65 86 94 63 93 64 99 93]
排序 Sort
Sort 方法根據一定隊則對集合中的元素進行排序,引數為比較函式,形如func(o1,o2 T) bool,引數為集合中的兩個元素,返回值為第一引數是否小於第二個引數。排序演算法使用sort中的排序演算法。 Sort 方法是中間操作。
func (s *stream) Sort(lessFunc interface{}) *stream
例子:
students := createStudents()
stream, _ := New(students)
stream.Sort(func(s1, s2 student) bool {
return s1.scores[0]+s1.scores[1]+s1.scores[2] > s2.scores[0]+s2.scores[1]+s2.scores[2]
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})
輸出:
{id:7, name:King, age:22,scores:[87 91 89]}
{id:10, name:Jim, age:20,scores:[64 99 93]}
{id:9, name:King, age:21,scores:[94 63 93]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:5, name:Mask, age:15,scores:[68 78 67]}
{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:3, name:Lee, age:15,scores:[62 69 68]}
去重 Distinct
Distinct 方法會對集合中的元素進行比較,並將重複的元素過濾掉. 引數為比較函式,形如 func(o1,o2 T) bool,引數為集合中的兩個元素,返回值為兩個元素是否相等。 Distinct 方法是中間操作。
func (s *stream) Distinct(equalFunc interface{}) *stream
例子:
students := createStudents()
stream, _ := New(students)
stream.Map(func(s student) string {
return s.name
}).Distinct(func(p1, p2 string) bool {
return p1 == p2
}).ForEach(func(s string) {
fmt.Printf("\t%s\n", s)
})
輸出:
Kate
Lee
Lucy
Mask
Jim
King
Jack
提取 Peek
Peek 方法遍歷集合的每個元素,執行一定的處理,處理函式形如func(o T),引數為集合每一個元素,沒有返回值。 Peek 方法和 ForEach 方法的區別,它是一箇中間操作,可以繼續呼叫Stream的其他方法。
func (s *stream) Peek(peekFunc interface{}) *stream
例子:
students := createStudents()
stream, _ := New(students)
stream.Filter(func(s student) bool {
return s.age%2 == 0
}).Call(func() {
fmt.Println("\tfilter by age % 2 == 0")
}).Peek(func(s student) {
fmt.Printf("\t%s\n", s.String())
}).Filter(func(s student) bool {
return s.age > 18
}).Call(func() {
fmt.Println("\tfilter by age > 18")
}).Peek(func(s student) {
fmt.Printf("\t%s\n", s.String())
}).Exec()
輸出:
filter by age % 2 == 0
{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:10, name:Jim, age:20,scores:[64 99 93]}
filter by age > 18
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:10, name:Jim, age:20,scores:[64 99 93]}
呼叫 Call
Call 方法可以在Stream物件執行過程中拿到集合的所有資料,可以對中間結果做一些處理,引數為處理函式,形如func(o []T),引數為整個集合的資料。 Call 方法為中間操作。
func (s *stream) Call(callFunc interface{}) *stream
檢查 Check
Check 方法可以在Stream物件執行過程中檢查是否需要進行後續操作,引數為判斷函式,形如func(o []T) bool,引數為整個集合的資料,返回值為是否繼續處理資料。 Check 方法為中間操作。 Check 方法與Call 方法的區分是,它可以終止整個Steam的執行。
func (s *stream) Check(checkFunc interface{}) *stream
限制 Limit
Limit 方法可以限制集合中元素的數量,引數為顯示的數量。 Limit 方法為中間操作。
func (s *stream) Limit(num int) *stream
例子:
students := createStudents()
stream, _ := New(students)
stream.Limit(5).Call(func() {
fmt.Println("\tlimit by 5")
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})
輸出:
limit by 5
{id:1, name:Kate, age:16,scores:[67 79 61]}
{id:2, name:Lee, age:22,scores:[80 76 80]}
{id:3, name:Lee, age:15,scores:[62 69 68]}
{id:4, name:Lucy, age:22,scores:[65 97 86]}
{id:5, name:Mask, age:15,scores:[68 78 67]}
跳過 Skip
Skip 方法可以在處理過程中跳過指定數目的元素,引數為跳過的數量.
func (s *stream) Skip(num int) *stream
例子:
stream.Skip(5).Call(func() {
fmt.Println("\tskip by 5")
}).ForEach(func(s student) {
fmt.Printf("\t%s\n", s.String())
})
輸出:
skip by 5
{id:6, name:Jim, age:20,scores:[68 90 75]}
{id:7, name:King, age:22,scores:[87 91 89]}
{id:8, name:Jack, age:16,scores:[91 65 86]}
{id:9, name:King, age:21,scores:[94 63 93]}
{id:10, name:Jim, age:20,scores:[64 99 93]}
全部匹配 AllMatch
AllMatch 判斷集合中的元素是否都符合條件,需要提供一個判斷函式,形如 func(o T) bool , 引數為集合中的元素,返回值為是否條件。 AllMatch 方法為終止操作,返回值為是否所有都符合條件。
func (s *stream) AllMatch(matchFunc interface{}) bool
任一匹配 AnyMatch
AnyMatch 判斷集合中的元素是否有任一元素符合條件,需要提供一個判斷函式,形如 func(o T) bool ,引數為集合中的元素,返回值為是否條件。 AnyMatch 方法為終止操作,返回值為是否有任一元素符合條件。
func (s *stream) AnyMatch(matchFunc interface{}) bool
全不匹配 NoneMatch
NoneMatch 判斷集合中的元素是否所有元素都不符合條件,需要提供一個判斷函式,形如 func(o T) bool ,引數為集合中的元素,返回值為是否條件。 NoneMatch 方法為終止操作,返回值為是否所有元素都不符合條件。
func (s *stream) NoneMatch(matchFunc interface{}) bool
例子:
students := createStudents()
stream, _ := New(students)
r1 := stream.AllMatch(func(s student) bool {
return s.age > 20
})
stream.Reset()
r2 := stream.AnyMatch(func(s student) bool {
return s.name == "Jim"
})
stream.Reset()
r3 := stream.NoneMatch(func(s student) bool {
return s.scores[0]+s.scores[1]+s.scores[2] > 270
})
fmt.Printf("\tAllMatch: %t, AnyMatch: %t, NoneMatch: %t \n", r1, r2, r3)
輸出:
AllMatch: false, AnyMatch: true, NoneMatch: true
計數 Count
Count 返回集合中元素的數量。 Count 為終止操作。
func (s *stream) Count() int
例子:
students := createStudents()
stream, _ := New(students)
r := stream.Count()
fmt.Printf("\t%d\n", r)
輸出:
10
分組 Group
Group 方法可以根據規則,將集合中的元素進行分組,需要提供一個分組函式,形如func(o T1) (key T2,value T3),引數為集合中的元素,返回值為分組的key和value。 Group 方法為終止操作,返回值為分組的map。
func (s *stream) Group(groupFunc interface{}) interface{}\
最大值 Max
Max 方法返回集合中最大的元素,需要提供一個比較函式,形如func(o1,o2 T) bool,引數為集合中的兩個元素,返回值為第一引數是否小於第二個引數。 Max 方法為終止操作。
func (s *stream) Max(lessFunc interface{}) interface{}
最小值 Min
Min 方法返回集合中最大的元素,需要提供一個比較函式,形如func(o1,o2 T) bool,引數為集合中的兩個元素,返回值為第一引數是否小於第二個引數。 Min 方法為終止操作。
func (s *stream) Min(lessFunc interface{}) interface{}
例子:
students := createStudents()
stream, _ := New(students)
r1 := stream.Max(func(s1, s2 student) bool {
return s1.scores[0]+s1.scores[1]+s1.scores[2] < s2.scores[0]+s2.scores[1]+s2.scores[2]
})
stream.Reset()
r2 := stream.Min(func(s1, s2 student) bool {
return s1.scores[0]+s1.scores[1]+s1.scores[2] < s2.scores[0]+s2.scores[1]+s2.scores[2]
})
fmt.Printf("\tMax: %v, Min: %v \n", r1, r2)
輸出:
Max: {7 King 22 [87 91 89]}, Min: {3 Lee 15 [62 69 68]}
最先匹配 First
First 方法返回第一個符合條件的元素,需要提供一個匹配函式,形如 func(o T) bool,引數為集合中的元素,返回值表示該元素是否匹配條件。 First 為終止操作。
func (s *stream) First(matchFunc interface{}) interface{}
最後匹配 Last
First 方法返回第一個符合條件的元素,需要提供一個匹配函式,形如 func(o T) bool,引數為集合中的元素,返回值表示該元素是否匹配條件。 First 為終止操作。
func (s *stream) Last(matchFunc interface{}) interface{}
規約 Reduce
Reduce 方法可以基於一個初始值,遍歷將規約函式應用於集合中的每個元素,得到最終結果,規約函式形如 func(r T2,o T) T2,引數為前面的元素計算結果和當前元素,返回值為新的結果。 Reduce 為終止操作,返回值為規約計算後的結果。
func (s *stream) Reduce(initValue interface{}, reduceFunc interface{}) interface{}
例子:
students := createStudents()
stream, _ := New(students)
r := 0
r = stream.Map(func(s student) int {
return s.scores[0]
}).Reduce(r, func(sum int, i int) int {
return sum + i
}).(int)
fmt.Printf("\t%d\n", r)
輸出:
746
相關文章
- 使用JWT做RESTful API的身份驗證-Go語言實現JWTRESTAPIGo
- Go語言實現RPCGoRPC
- Java Stream API:實現 Kruskal 演算法JavaAPI演算法
- go語言實現自己的RPC:go rpc codecGoRPC
- go語言實現掃雷Go
- Go語言map的底層實現Go
- Go語言實現TCP通訊GoTCP
- go語言依賴注入實現Go依賴注入
- GO語言 實現埠掃描Go
- go語言實現ssh打隧道Go
- Go語言interface底層實現Go
- Go語言RESTful JSON API建立GoRESTJSONAPI
- Go語言實現HTTPS加密協議GoHTTP加密協議
- 檔案複製(Go語言實現)Go
- 線性迴歸 go 語言實現Go
- 漏桶、令牌桶限流的Go語言實現Go
- 【語言】Java 日期 API 的使用技巧JavaAPI
- Go 語言實現 QQ 掃碼登陸Go
- Go語言實現位元組記錄鎖Go
- Go 語言實現解析器翻譯Go
- go語言使用切片實現線性表Go
- Go語言實現簡單的反序列化Go
- Golang | Go語言多型的實現與interface使用Golang多型
- Java8中的Stream APIJavaAPI
- Java8的Stream API使用JavaAPI
- Go 語言實踐(一)Go
- Go 語言實戰 GraphQLGo
- Java 函數語言程式設計(三)流(Stream)Java函數程式設計
- 使用 Go 語言實現簡單的文字識別(OCR)Go
- Go語言實現設計模式之命令模式Go設計模式
- 快收藏!最全GO語言實現設計模式Go設計模式
- Go語言實現布穀鳥過濾器Go過濾器
- java語言實現二叉樹Java二叉樹
- 學Go語言能找到實習嗎,年前閒聊Go和JavaGoJava
- 【命令設計模式詳解】C/Java/JS/Go/Python/TS不同語言實現設計模式JavaJSGoPython
- 【建造者設計模式詳解】Java/JS/Go/Python/TS不同語言實現設計模式JavaJSGoPython
- 【橋接設計模式詳解】Java/JS/Go/Python/TS不同語言實現橋接設計模式JavaJSGoPython
- JDK 8 新特性之函數語言程式設計 → Stream APIJDK函數程式設計API