什么是Stream
生產(chǎn)線
Stream就像處理生產(chǎn)流水線一樣去工作,傳送帶就是Stream的管道,每個(gè)工廠關(guān)注直接的生產(chǎn),將上游產(chǎn)品加工成下游需要的產(chǎn)品。為什么Stream比傳統(tǒng)的處理方式好呢?我們都知道,傳統(tǒng)的處理中,每一步我們都需要通過循環(huán)控制,邏輯控制,解包,重新裝箱這些工作。
非生產(chǎn)線示意處理圖
這些步驟讓我們的程序的業(yè)務(wù)邏輯支離破碎,經(jīng)常處理數(shù)據(jù)類的小伙伴尤為痛苦。幸運(yùn)的是,Java8為我們引入了Stream,使用Stream后我們只關(guān)注數(shù)據(jù)處理邏輯,其他的事情交給流處理對應(yīng)的方法來完成。
創(chuàng)建數(shù)據(jù)流
指北君先為大家介紹如何創(chuàng)建Stream,這里有非常多的方式,需要注意一點(diǎn)就是:流一旦創(chuàng)建后,修改創(chuàng)建的源不會影響已經(jīng)創(chuàng)建的Stream中的數(shù)據(jù)。
- 空流 為了避免出現(xiàn)空指針異常,系統(tǒng)提供一個(gè)靜態(tài)方法提供空流。
public void createStream() {
Stream< String > myStream = Stream.empty();
}
- 通過數(shù)組對象創(chuàng)建流
public void createStream() {
Integer[] arr = new Integer[]{1,2,3};
Stream< Integer > stream1 = Arrays.stream(arr);
Stream< Integer > stream2 = Arrays.stream(arr, 0, 2);
}
- 通過集合對象創(chuàng)建流
public void createStream() {
Collection< String > collection = Arrays.asList("a", "b", "c");
Stream< String > stream1 = collection.stream();
}
支持多種集合:List,Set,Map等實(shí)現(xiàn)了Collection接口的集合對象。
- 通過builder創(chuàng)建
public void createStream() {
Stream< Long > stream1 =
Stream.< Long >builder().add(1L).add(2L).add(3L).build();
}
- 通過generate生成
public void createStream() {
Random r = new Random();
Stream< Long > stream =
Stream.generate(() - > r.nextLong()).limit(10);
}
按照提供給generate的Supplier邏輯生成數(shù)據(jù),通過limit限制生成的數(shù)據(jù)量
- 通過Stream.iterate創(chuàng)建
public void createStream() {
Stream< Integer > stream = Stream.iterate(1, n - > n * n).limit(20);
}
iterate提供兩種方法來滿足我們比較常用的迭代生成邏輯
- iterate(final T seed, final UnaryOperatorf)
- iterate(T seed, Predicate hasNext, UnaryOperatornext)
- 原生類型生成 通過對應(yīng)的IntStream,LongStream,DoubleStream類中提供的方法來獲取,包含常用的方法
- builder()
- empty()
- of()
- iterate()
- generate()
- range()
- concat()
- 其他地方 這里介紹兩處:字符分割匹配和文件行數(shù)據(jù)
String.chars()返回IntStream
Files.lines()返回通過行分割的字符內(nèi)容
流的使用機(jī)制(重要事項(xiàng))
我們通過上面的方法創(chuàng)建好流后,就可以對流進(jìn)行相關(guān)的業(yè)務(wù)邏輯處理了,需要注意:如果我們重復(fù)對一個(gè)流進(jìn)行操作,就會出錯,系統(tǒng)會爆出IllegalStateException異常,這是因?yàn)镾tream設(shè)計(jì)為不可重用的模式。流的下一個(gè)環(huán)節(jié)都是對當(dāng)前環(huán)節(jié)處理后新生成流的處理。
流的執(zhí)行順序
采用Stream方式進(jìn)行多個(gè)邏輯處理時(shí),他們之間的執(zhí)行順序是什么樣的呢?指北君為了展示效果,寫了一段測試代碼:
public void exeOrder() {
List< String > list = Arrays.asList("data_1","data_2", "data_3", "data_12");
list.stream().filter(x - > {
System.out.println("filter() was called: " + x);
return x.contains("2");
}).map(x - > {
System.out.println("map() was called: " + x);
return x.toUpperCase();
}).forEach(x- >System.out.println("forEach() was called: " + x));
}
執(zhí)行結(jié)果如下:
filter() was called: data_1
filter() was called: data_2
map() was called: data_2
forEach() was called: data_2
filter() was called: data_3
filter() was called: data_12
map() was called: data_12
forEach() was called: data_12
從示例代碼的打印的順序中我們可以發(fā)現(xiàn):流處理的順序不是以代碼順序(執(zhí)行完一步再到下一步),而是按照數(shù)據(jù)處理完一個(gè)單位數(shù)據(jù)的所有環(huán)節(jié)再處理下一個(gè)數(shù)據(jù),見下面的動態(tài)示意圖:
Stream處理順序
既然我們了解流的處理順序,也能理解某些流操作會提前結(jié)束流處理的,比如findFirst(),在處理完第一個(gè)符合條件的數(shù)據(jù)后,后續(xù)的數(shù)據(jù)不會參與任何一個(gè)環(huán)節(jié)的處理。
轉(zhuǎn)換處理
轉(zhuǎn)換處理時(shí)最常用的邏輯處理方式,介紹轉(zhuǎn)換處理的文章較多,這里不再一一詳細(xì)描述只是簡單列一下,轉(zhuǎn)換處理對應(yīng)大數(shù)據(jù)MapReduce中的Map處理
- distinct剔重
- filter過濾
- map轉(zhuǎn)換映射
- peek
- limit
- skip
合并處理(reduce)
對于Map-Reduce模型的reduce操作,國內(nèi)對這個(gè)詞翻譯不太統(tǒng)一,指北君就先稱之為合并處理吧。這里介紹兩個(gè)方法reduce和collect
- reduce 先來看一個(gè)reduce的示例
public void reduce() {
int sum = IntStream.range(1, 100).reduce(0, (a, b) - > a + b);
System.out.print(sum);
}
合并Stream中的所有值,合并的初始值為0,如果初始為0還可以省略初始值。reduce函數(shù)包含三部分關(guān)鍵信息:
- 初始值,指定合并操作的初始值
- 合并函數(shù)
- 合路器(函數(shù)),在并行(多線程)運(yùn)算時(shí)需要用到
下面是一個(gè)使用合路器的示例,在并行運(yùn)算時(shí)使用。
public void parallelReduce() {
int sum = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).parallelStream()
.reduce(0, (a, b) - > a + b, (a, b) - > {
return a + b;
});
System.out.println(sum);
}
這里指北君留一道思考題給大家,如果這里初始值0修改為10,最終的結(jié)果是多少?為什么是這種結(jié)果呢?
- collect 現(xiàn)在我們再來看collect,collect嚴(yán)格上說是reduce有些牽強(qiáng),因?yàn)槭欠駌educe在于collect中的執(zhí)行邏輯 比如這段:
List String > collector = list.stream().map(Product::getName).collect(Collectors.toList());
然后再看下面的例子:
String mergString = list.stream().map(Product::getName).collect(Collectors.joining(", ", "[", "]"));
還有其他對應(yīng)的方法:
- Collectors.averagingInt
- Collectors.summingInt
- Collectors.groupingBy
- Collectors.partitioningBy
各位小伙伴可以查看Collectors對應(yīng)的API,這里就不一一列舉了,總之,collect通過Collectors對象的API類完成合并處理。
-
接口
+關(guān)注
關(guān)注
33文章
8450瀏覽量
150726 -
JAVA
+關(guān)注
關(guān)注
19文章
2952瀏覽量
104489 -
字符
+關(guān)注
關(guān)注
0文章
232瀏覽量
25154 -
數(shù)據(jù)處理
+關(guān)注
關(guān)注
0文章
574瀏覽量
28509 -
Stream
+關(guān)注
關(guān)注
0文章
20瀏覽量
7956
發(fā)布評論請先 登錄
相關(guān)推薦
評論