學習Go語言時實現的集合操做工具庫,相似於Java 8 中新增的Stream API。因爲Go語言不支持泛型,因此基於反射實現。只用於學習目的,不要用於生產(PS:固然也不會有人用)。node
項目地址:github.com/tk103331/st…git
集合操做包括生成操做、中間操做和終止操做。 生成操做返回值是Steam對象,至關於數據的源頭,能夠調用Stream的其餘方法;中間操做返回值是Stream對象,能夠繼續調用Stream的方法,便可以鏈式調用方法;終止操做不能繼續調用方法。github
下面介紹下這個庫的API:算法
數據準備 後面的操做都是基於集合數據的,先準備一些測試數據。less
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
}
複製代碼
循環遍歷集合中的每個元素,須要提供一個包含一個參數的處理函數做爲參數,形如 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]}
複製代碼
It 方法能夠從一個迭代器中建立一個Stream對象,迭代器就是一個迭代產生數據的迭代函數,迭代函數形如 func(prev T) (next T,more bool),迭代函數的參數爲上一個元素的值,返回值是下一個元素的值,和是否還有更多元素。 It 方法是生成操做。測試
func It(initValue interface{}, itFunc interface{}) (*stream, error)
複製代碼
Sample:spa
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}
複製代碼
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 方法對集合中的元素進行過濾,篩選出符合條件的元素,須要提供一個過濾函數,過濾函數形如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 方法能夠將集合中的每一個元素映射爲新的值,從而獲得一個新的集合,須要提供一個映射函數,形如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 方法能夠將集合中每一個元素映射爲多個元素,返回新的集合包含映射的全部元素。須要提供一個映射函數,形如 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 方法根據必定隊則對集合中的元素進行排序,參數爲比較函數,形如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 方法會對集合中的元素進行比較,並將重複的元素過濾掉. 參數爲比較函數,形如 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 方法遍歷集合的每一個元素,執行必定的處理,處理函數形如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 方法能夠在Stream對象執行過程當中拿到集合的全部數據,能夠對中間結果作一些處理,參數爲處理函數,形如func(o []T),參數爲整個集合的數據。 Call 方法爲中間操做。
func (s *stream) Call(callFunc interface{}) *stream
複製代碼
Check 方法能夠在Stream對象執行過程當中檢查是否須要進行後續操做,參數爲判斷函數,形如func(o []T) bool,參數爲整個集合的數據,返回值爲是否繼續處理數據。 Check 方法爲中間操做。 Check 方法與Call 方法的區分是,它能夠終止整個Steam的執行。
func (s *stream) Check(checkFunc interface{}) *stream
複製代碼
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 方法能夠在處理過程當中跳過指定數目的元素,參數爲跳過的數量.
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 判斷集合中的元素是否都符合條件,須要提供一個判斷函數,形如 func(o T) bool , 參數爲集合中的元素,返回值爲是否條件。 AllMatch 方法爲終止操做,返回值爲是否全部都符合條件。
func (s *stream) AllMatch(matchFunc interface{}) bool
複製代碼
AnyMatch 判斷集合中的元素是否有任一元素符合條件,須要提供一個判斷函數,形如 func(o T) bool ,參數爲集合中的元素,返回值爲是否條件。 AnyMatch 方法爲終止操做,返回值爲是否有任一元素符合條件。
func (s *stream) AnyMatch(matchFunc interface{}) bool
複製代碼
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 爲終止操做。
func (s *stream) Count() int
複製代碼
例子:
students := createStudents()
stream, _ := New(students)
r := stream.Count()
fmt.Printf("\t%d\n", r)
複製代碼
輸出:
10
複製代碼
Group 方法能夠根據規則,將集合中的元素進行分組,須要提供一個分組函數,形如func(o T1) (key T2,value T3),參數爲集合中的元素,返回值爲分組的key和value。 Group 方法爲終止操做,返回值爲分組的map。
func (s *stream) Group(groupFunc interface{}) interface{}\
複製代碼
Max 方法返回集合中最大的元素,須要提供一個比較函數,形如func(o1,o2 T) bool,參數爲集合中的兩個元素,返回值爲第一參數是否小於第二個參數。 Max 方法爲終止操做。
func (s *stream) Max(lessFunc interface{}) interface{}
複製代碼
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 方法返回第一個符合條件的元素,須要提供一個匹配函數,形如 func(o T) bool,參數爲集合中的元素,返回值表示該元素是否匹配條件。 First 爲終止操做。
func (s *stream) First(matchFunc interface{}) interface{}
複製代碼
First 方法返回第一個符合條件的元素,須要提供一個匹配函數,形如 func(o T) bool,參數爲集合中的元素,返回值表示該元素是否匹配條件。 First 爲終止操做。
func (s *stream) Last(matchFunc interface{}) interface{}
複製代碼
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
複製代碼