到現(xiàn)在為止,筆者不敢給流下定義,從概念來講他應(yīng)該也是一種數(shù)據(jù)元素才是。可是在我們前面的代碼例子中我們可以看到他更多的好像在表示他是一組處理數(shù)據(jù)的行為組合。這讓筆者很難去理解他的定義。所以筆者不表態(tài)。各位同志自行理解吧。
在沒有流以前,處理集合里面的數(shù)據(jù)一般都會(huì)用到顯示的迭代器。用一下前面學(xué)生的例子吧。目標(biāo)是獲得學(xué)分大于5的前倆位同學(xué)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
package com.aomi; import java.util.arraylist; import java.util.iterator; import java.util.list; import static java.util.stream.collectors.tolist; public class main { public static void main(string[] args) { // todo auto-generated method stub list<student> stus = getsources(); iterator<student> ite = stus.iterator(); list<string> names = new arraylist<>(); int limit = 2 ; while (ite.hasnext() && limit > 0 ) { student stu = ite.next(); if (stu.getscore() > 5 ) { names.add(stu.getname()); limit--; } } for (string name : names) { system.out.println(name); } } public static list<student> getsources() { list<student> students = new arraylist<>(); student stu1 = new student(); stu1.setname( "lucy" ); stu1.setsex( 0 ); stu1.setphone( "13700227892" ); stu1.setscore( 9 ); student stu2 = new student(); stu2.setname( "lin" ); stu2.setsex( 1 ); stu2.setphone( "15700227122" ); stu2.setscore( 9 ); student stu3 = new student(); stu3.setname( "lili" ); stu3.setsex( 0 ); stu3.setphone( "18500227892" ); stu3.setscore( 8 ); student stu4 = new student(); stu4.setname( "dark" ); stu4.setsex( 1 ); stu4.setphone( "16700555892" ); stu4.setscore( 6 ); students.add(stu1); students.add(stu2); students.add(stu3); students.add(stu4); return students; } } |
如果用流的話是這樣子的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public static void main(string[] args) { // todo auto-generated method stub list<student> stus = getsources(); list<string> names = stus.stream() .filter(st -> st.getscore() > 5 ) .limit( 2 ) .map(st -> st.getname()) .collect(tolist()); for (string name : names) { system.out.println(name); } } |
把這倆段代碼相比較主要是為了說明一個(gè)概念:以前做法都是在外部迭代,最為體現(xiàn)就是筆者在外面定義了一個(gè)集合names 。而流卻什么也沒有,現(xiàn)在我們應(yīng)該能清楚感受到流是在內(nèi)部迭代。也就是說流已經(jīng)幫你做好了迭代。我們只要傳入相關(guān)的函數(shù)就可以得到想要的結(jié)果。至于內(nèi)部迭代的好處,筆者沒有辦法親身的感受,唯一的感覺就是代碼變的簡單明了了。但是官方說stream庫為了我們?cè)趦?nèi)部迭代里面做了很多優(yōu)化和充公利用性能的操作。比如并行操作。所以筆者就聽官方了。
事實(shí)上,在用流的過程中,我們用到很多方法函數(shù)。比如上面的limit方法,filter方法等。這個(gè)定義為流操作。但是不管是什么操作,你必須要有一個(gè)數(shù)據(jù)源吧。總結(jié)如下:
- 數(shù)據(jù)源:用于生成流的數(shù)據(jù),比如集合。
- 流操作:類似于limit方法,filter方法。
流還有一種特點(diǎn)——部分流操作是沒有執(zhí)行的。一般都是在collect函數(shù)執(zhí)行的時(shí)候,才開始執(zhí)行個(gè)個(gè)函數(shù)。所以我們可以細(xì)分一下流操作:
- 數(shù)據(jù)源:用于生成流的數(shù)據(jù),比如集合。
- 中間操作:類似于limit方法,filter方法。這些操作做變了一個(gè)操作鏈,有一點(diǎn)流水線的概念。
- 終端操作:執(zhí)行上面的操作鏈。比如collect函數(shù)。
從上面的講解我們就可以感覺流好像是先收集相關(guān)的目標(biāo)操作,什么意思呢?就是先把要做的事情計(jì)劃一下,最后一聲令下執(zhí)行。而下這個(gè)命令是collect函數(shù)。這一點(diǎn)跟.net的linq是很像的。同時(shí)記得他只能執(zhí)行一次。也就是說這個(gè)流執(zhí)行一次之后,就不可能在用了。
筆者列一下以前的用到的函數(shù)
foreach:終端
collect:終端
count:終端
limit:中間
filter:中間
map:中間
sorted:中間
到目前為止我們用到的流都是通過集合來建一個(gè)流。筆者對(duì)此從來沒有講過。現(xiàn)在筆者來講些構(gòu)建流的方式。
在stream庫里面為我們提供了這樣子一個(gè)方法——stream.of
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.aomi; import java.util.optional; import java.util.stream.stream; public class main { public static void main(string[] args) { // todo auto-generated method stub stream stream = stream.of( "i" , "am" , "aomi" ); optional<string> firstword = stream.findfirst(); if (firstword.ispresent()) { system.out.println( "第一個(gè)字:" +firstword.get()); } } } |
運(yùn)行結(jié)果:
去看一下of方法的代碼。如下
1
2
3
|
public static <t> stream<t> of(t... values) { return arrays.stream(values); } |
說明我們可能指定一個(gè)類型來建一個(gè)流。上面可以修改為
1
|
stream<string> stream = stream.of( "i" , "am" , "aomi" ); |
findfirst函數(shù)用于表示返回第一個(gè)值。那就是可能數(shù)據(jù)源是一個(gè)空呢?所以他有可以會(huì)返回null。所以就是用一個(gè)叫optional類的表示可以為空。這樣子我們就可以用optional類的方法進(jìn)一步做安全性的操作。比如判斷有沒有值(ispresent())
筆者想要建一個(gè)int類型的數(shù)組流玩玩。為了方便筆者便試給一下上面的代碼。卻發(fā)現(xiàn)報(bào)錯(cuò)了。
如果我把int改為integer呢?沒有問題了。所以注意要用引用類型的。int類型對(duì)應(yīng)為integer類型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.aomi; import java.util.optional; import java.util.stream.stream; public class main { public static void main(string[] args) { // todo auto-generated method stub stream<integer> stream = stream.of( 1 , 2 , 9 ); optional<integer> firstword = stream.findfirst(); if (firstword.ispresent()) { system.out.println( "第一個(gè)字:" +firstword.get()); } } } |
運(yùn)行結(jié)果:
那想要用int類型呢?什么辦呢?改改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.aomi; import java.util.optionalint; import java.util.stream.intstream; public class main { public static void main(string[] args) { // todo auto-generated method stub intstream stream = intstream.of( 1 , 2 , 9 ); optionalint firstword = stream.findfirst(); if (firstword.ispresent()) { system.out.println( "第一個(gè)字:" +firstword.getasint()); } } } |
運(yùn)行結(jié)果:
我們以上面的例子來一個(gè)猜測(cè):是不是double類型,只要修改為doublestream就行呢?試試。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.aomi; import java.util.optionaldouble; import java.util.stream.doublestream; public class main { public static void main(string[] args) { // todo auto-generated method stub doublestream stream = doublestream.of( 1.3 , 2.3 , 9.5 ); optionaldouble firstword = stream.findfirst(); if (firstword.ispresent()) { system.out.println( "第一個(gè)字:" +firstword.getasdouble()); } } } |
運(yùn)行結(jié)果:
結(jié)果很明顯,我們的猜測(cè)是對(duì)的。所以見意如果你操作的流是一個(gè)int或是double的話,請(qǐng)進(jìn)可能的用xxxstream 來建流。這樣子在流的過程中不用進(jìn)行拆裝和封裝了。必竟這是要性能的。在看一下如果數(shù)據(jù)源是一個(gè)數(shù)組的情況我們?nèi)绾紊闪髂兀?/p>
1
2
3
4
5
6
7
8
|
public static collector<charsequence, ?, string> joining(charsequence delimiter, charsequence prefix, charsequence suffix) { return new collectorimpl<>( () -> new stringjoiner(delimiter, prefix, suffix), stringjoiner::add, stringjoiner::merge, stringjoiner::tostring, ch_noid); } |
在看一個(gè)叫tolist函數(shù)的代碼。
1
2
3
4
5
6
|
public static <t> collector<t, ?, list<t>> tolist() { return new collectorimpl<>((supplier<list<t>>) arraylist:: new , list::add, (left, right) -> { left.addall(right); return left; }, ch_id); } |
我們發(fā)現(xiàn)他會(huì)共同的返回一個(gè)collector類型。從上面我們就可以知道他的任務(wù)就是用去處理最后數(shù)據(jù)。我們把他定為收集器。讓我們看一下收集器的接口代碼吧;
1
2
3
4
5
6
7
8
9
10
11
12
|
public interface collector<t, a, r> { supplier<a> supplier(); biconsumer<a, t> accumulator(); binaryoperator<a> combiner(); function<a, r> finisher(); set<characteristics> characteristics(); } |
光看前面四個(gè)方法是不是有一點(diǎn)熟悉的感覺。想要說明這個(gè)五個(gè)方法的作用。就必須明白一個(gè)概念——并行歸約。前面筆者講過流是一個(gè)內(nèi)部迭代,也說stream庫為我們做一個(gè)很多優(yōu)化的事情。其中一個(gè)就是并行。他用到了java 7引入的功能——分支/合并框架。也就是說流會(huì)以遞歸的方式拆分成很多子流,然后子流可以并行執(zhí)行。最后在倆倆的子流的結(jié)果合并成一個(gè)最終結(jié)果。而這倆倆合并的行為就叫歸約 。如圖下。引用于《java8實(shí)戰(zhàn)》
我們必須根據(jù)圖上的意思來走。子流的圖里面會(huì)調(diào)用到collector類的三個(gè)方法。
- supplier方法:用創(chuàng)建數(shù)據(jù)存儲(chǔ)的地方。
- accumulator方法:用于子流執(zhí)行過程的迭代工作。即是遍歷每一項(xiàng)都會(huì)執(zhí)行。所以可以這里做一些工作。
- finisher方法:返回最后的結(jié)果,你可以在這里進(jìn)一步處理結(jié)果。
每一個(gè)子流結(jié)束這之后,就是倆倆合并。這個(gè)時(shí)候就要看流的機(jī)制圖了。
- combiner方法:會(huì)傳入每一個(gè)子流的結(jié)果過來,我們就可以在這里在做一些工作。
- finisher方法:返回最后的結(jié)果。同上面子流的一樣子。
好像沒有characteristics什么事情。不是這樣子的。這個(gè)方法是用來說明當(dāng)前這個(gè)流具備哪些優(yōu)化。這樣子執(zhí)行流的時(shí)候,就可以很清楚的知道要以什么樣子的方式執(zhí)行了。比如并行。
他是一個(gè)enum類。值如下
- unordered:這個(gè)表示執(zhí)行過程中結(jié)果不受歸約和遍歷的影響
- concurrent:表示可以多個(gè)線和調(diào)用accumulator方法。并且可以執(zhí)行并行。當(dāng)然前無序數(shù)據(jù)的才并行。除非收集器標(biāo)了unordered。
- identity_finish:表示這是一個(gè)恒等函數(shù),就是做了結(jié)果也一樣子。不用做了可以跳過了。
由了上面的講說明,我們?cè)趤韺懸粋€(gè)自己的收集器吧——去除相同的單詞
distinctwordcollector類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
package com.aomi; import java.util.arraylist; import java.util.collections; import java.util.enumset; import java.util.list; import java.util.set; import java.util.function.biconsumer; import java.util.function.binaryoperator; import java.util.function.function; import java.util.function.supplier; import java.util.stream.collector; public class distinctwordcollector implements collector<string, list<string>, list<string>> { @override public supplier<list<string>> supplier() { // todo auto-generated method stub return () -> new arraylist<string>(); } /** * 子流的處理項(xiàng)的過程 */ @override public biconsumer<list<string>, string> accumulator() { // todo auto-generated method stub return (list<string> src, string val) -> { if (!src.contains(val)) { src.add(val); } }; } /** * 倆倆并合的執(zhí)行函數(shù) */ @override public binaryoperator<list<string>> combiner() { // todo auto-generated method stub return (list<string> src1, list<string> src2) -> { for (string val : src2) { if (!src1.contains(val)) { src1.add(val); } } return src1; }; } @override public function<list<string>, list<string>> finisher() { // todo auto-generated method stub return function.identity(); } @override public set<characteristics> characteristics() { // todo auto-generated method stub return collections.unmodifiableset(enumset.of(characteristics.identity_finish, characteristics.concurrent)); } } |
main:
1
2
3
4
5
6
7
8
9
10
11
|
public static void main(string[] args) { // todo auto-generated method stub list<string> words = arrays.aslist( "aomi" , "lili" , "lucy" , "aomi" , "nono" ); list<string> vals = words.stream().collect( new distinctwordcollector()); for (string val : vals) { system.out.println(val); } } |
運(yùn)行結(jié)果
結(jié)果確定就是我們想要的——去掉了重復(fù)的aomi
以上所述是小編給大家介紹的java8流的概念和收集器詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)服務(wù)器之家網(wǎng)站的支持!