Stream知識手冊

1.流(Stream)

? ??流是Java API的新成員,它允許你以聲明性方式處理數(shù)據(jù)集合(通過查詢語句來表達(dá),而不是臨時編寫一個實(shí)現(xiàn))。就現(xiàn)在來說,你可以把它們看成遍歷數(shù)據(jù)集的高級迭代器。此外,流還可以透明地并行處理。

? ? 流到底是什么呢?簡短的定義就是“從支持?jǐn)?shù)據(jù)處理操作的源生成的元素序列”

? ? 1.元素序列——就像集合一樣,流也提供了一個接口,可以訪問特定元素類型的一組有序值。

? ? 2.源——流會使用一個提供數(shù)據(jù)的源,如集合、數(shù)組或輸入/輸出資源。

? ? 3.數(shù)據(jù)處理操作——常用操作,如filter、map、reduce、find、match、sort等且支持并行

? ? 4.流水線——很多流操作本身會返回一個流,這樣多個操作就可以鏈接起來,形成一個大的流水線

? ? 5.內(nèi)部迭代——與使用迭代器顯式迭代的集合不同,流的迭代操作是在背后進(jìn)行的


? ? 概念代碼:


流示例

? ??在本例中,我們先是對menu調(diào)用stream方法,由菜單得到一個流。數(shù)據(jù)源是菜肴列表(菜單),它給流提供一個元素序列。接下來,對流應(yīng)用一系列數(shù)據(jù)處理操作:filter、map、limit和collect。除了collect之外,所有這些操作都會返回另一個流,這樣它們就可以接成一條流水線,于是就可以看作對源的一個查詢。最后,collect操作開始處理流水線,并返回結(jié)果(它和別的操作不一樣,因?yàn)樗祷氐牟皇橇?,在這里是一個List)。在調(diào)用collect之前,沒有任何結(jié)果產(chǎn)生,實(shí)際上根本就沒有從menu里選擇元素。請注意,和迭代器類似,流只能遍歷一次


2.流操作

????Stream的操作可以分為兩大類:

????????1是filter、map和limit可以連成一條流水線。

? ? ? ? ?2是collect觸發(fā)流水線執(zhí)行并關(guān)閉它。

? ??流的使用一般包括三件事:

? ? ? ? 1.一個數(shù)據(jù)源(如集合)來執(zhí)行一個查詢;

? ? ? ? 2.一個中間操作鏈,形成一條流的流水線;

? ? ? ? 3.一個終端操作,執(zhí)行流水線,并能生成結(jié)果。

篩選和切片

????filter:?該操作會接受一個謂詞(一個返回boolean的函數(shù))作為參數(shù),并返回一個包括所有符合謂詞的元素的流

? ??distinct:它會返回一個元素各異(根據(jù)流所生成元素的hashCode和equals方法實(shí)現(xiàn))的流。

? ??limit(n):,該方法會返回一個不超過給定長度的流。所需的長度作為參數(shù)傳遞給limit。

? ??skip(n):返回一個扔掉了前n個元素的流。如果流中元素不足n個,則返回一個空流。

映射

? ??map:它會接受一個函數(shù)(Function)作為參數(shù)。這個函數(shù)會被應(yīng)用到每個元素上,并將其映射成一個新的元素(使用映射一詞,是因?yàn)樗娃D(zhuǎn)換類似,但其中的細(xì)微差別在于它是“創(chuàng)建一個新版本”而不是去“修改”).。

? ??flatMap:將各個生成流扁平,即扁平化多個流化為單個流。

查找和匹配

? ??另一個常見的數(shù)據(jù)處理套路是看看數(shù)據(jù)集中的某些元素是否匹配一個給定的屬性。StreamAPI通過allMatch、anyMatch、noneMatch、findFirst和findAny方法提供了這樣的工具。

歸約

? ? 歸約操作使用reduce方法,reduce接受兩個參數(shù),一個是初始值,一個是BinaryOperator<T>來將兩個元素結(jié)合起來產(chǎn)生一個新值。

? ??int sum = numbers.stream().reduce(0, Integer::sum);? ? //求合

? ??Optional<Integer> max = numbers.stream().reduce(Integer::max);? ? 求最大值

? ??reduce還有一個重載的變體,它不接受初始值,但是會返回一個Optional對象

? ??Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));

無狀態(tài)和有狀態(tài)

? ??諸如map或filter等操作會從輸入流中獲取每一個元素,并在輸出流中得到0或1個結(jié)果。這些操作一般都是無狀態(tài)的

? ??但諸如reduce、sum、max等操作需要內(nèi)部狀態(tài)來累積結(jié)果。不管流中有多少元素要處理,內(nèi)部狀態(tài)都是有界的。我們把這些操作叫作有狀態(tài)操作。

中間操作與終端操作

構(gòu)建流

? ? 1:Stream靜態(tài)方法Stream.of(T t),通過顯式值創(chuàng)建一個流。它可以接受任意數(shù)量的參數(shù)。

? ??Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");

? ? 2:可以使用empty得到一個空流,如下所示:

? ??Stream<String> emptyStream = Stream.empty();

? ?:3:數(shù)組創(chuàng)建流,Arrays的靜態(tài)方法Arrays.stream從數(shù)組創(chuàng)建一個流。它接受一個數(shù)組作為參數(shù):

? ??int[] numbers = {2, 3, 5, 7, 11, 13};

? ??int sum = Arrays.stream(numbers).sum();

? ? 4:文件生成流,Java中用于處理文件等I/O操作的NIO API(非阻塞 I/O)已更新,以便利用Stream API。java.nio.file.Files中的很多靜態(tài)方法都會返回一個流。


? ?5: 函數(shù)生成流:Stream API提供了兩個靜態(tài)方法來從函數(shù)生成流:Stream.iterate和Stream.generate。這兩個操作可以創(chuàng)建所謂的無限流:不像從固定集合創(chuàng)建的流那樣有固定大小的流。

? ? iterate示例:

? ??Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);

? ??iterate方法接受一個初始值(在這里是0),還有一個依次應(yīng)用在每個產(chǎn)生的新值上的Lambda(UnaryOperator<t>類型)這里,我們使用Lambda n -> n + 2。。請注意,此操作將生成一個無限流——這個流沒有結(jié)尾,因?yàn)橹凳前葱栌?jì)算的,可以永遠(yuǎn)計(jì)算下去。需要使用limit方法來顯式限制流的大小。

? ??generate示例:

? ??Stream.generate(Math::random).limit(5).forEach(System.out::println);

? ??它接受一個Supplier<T>類型的Lambda提供新的值。


3.收集器

? ??預(yù)定義收集器

? ??預(yù)定義收集器,也就是那些可以從Collectors類提供的工廠方法(例如groupingBy)創(chuàng)建的收集器。它們主要提供了三大功能:

? ? 1.歸約和匯總? ? 2.元素分組????3.元素分區(qū)?

? ? ? 計(jì)數(shù)可以使用Collectors.counting()方法,Collectors.maxBy和Collectors.minBy,來計(jì)算流中的最大或最小值。匯總后的返回值為Optional<T>它是一個容器,可以包含也可以不包含值。

? ??Collectors類專門為匯總提供了一個工廠方法:Collectors.summingInt。它可接受一個把對象映射為求和所需int的函數(shù),并返回一個收集器。此外還有averagingInt求平均數(shù),summarizing求總和、平均值、最大值和最小值。

? ? 示例:list.stream().collect(Collectors.summingInt(Item::getNo)? ? //匯總

? ? 連接字符串,joining工廠方法返回的收集器會把對流中每一個對象應(yīng)用toString方法得到的所有字符串連接成一個字符串。

? ? 示例:list.stream().map(Item::getName).collect(Collectors.joining())? ? //連接?


廣義的歸約匯總(reducing)

? ??Collectors.reducing工廠方法需要三個參數(shù):

? ? 1.第一個參數(shù)是起始值,也是流中沒有元素時的返回值,所以很顯然對于數(shù)值和而言0是一個合適的值。

? ? 2.第二個參數(shù)是轉(zhuǎn)換函數(shù)

? ? 3.第三個參數(shù)是累積函數(shù)BinaryOperator,將兩個項(xiàng)目累積成一個同類型的值。

? ? 字符串連接改造示例1:?

? ??list.stream().collect(Collectors.reducing(new String() , Item::getName , (a,b)->{return a+b;}))? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 初始值? ? ? ? ? ? 轉(zhuǎn)換函數(shù)? ? ? ? ? ? ? 累加器

? ??字符串連接改造示例2:

? ??list.stream().map(Item::getName).collect(Collectors.reducing((a,b)->{return a + b;}))? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?只有累加器,當(dāng)不需要轉(zhuǎn)換時使用


分組(groupingBy)

? ??Collectors.groupingBy有三個重載方法 :

? ??groupingBy(Function?classifier)?

? ??groupingBy(Function?classifier,Collector?downstream)

? ??groupingBy(Function?classifier,Supplier?mapFactory,Collector?downstream)


簡單分組 groupingBy(Function?classifier)?,

????Function稱為分類函數(shù),它用來把流中的元素分成不同的組。分組操作的結(jié)果是一個Map,把分組函數(shù)返回的值作為映射的鍵,把流中所有具有這個分類值的項(xiàng)目的列表作為對應(yīng)的映射值。

? ? 示例:以Item的name進(jìn)行分組

? ??Map<String, List<Item>> collect = list.stream().collect(Collectors.groupingBy(Item::getName));


二級分組?groupingBy(Function?classifier,Collector?downstream)

? ??? ??要實(shí)現(xiàn)多級分組,我們可以使用一個由雙參數(shù)版本的Collectors.groupingBy工廠方法創(chuàng)建的收集器,它除了普通的分類函數(shù)之外,還可以接受collector類型的第二個參數(shù)。

? ? 示例:以item的no為奇偶數(shù)進(jìn)行分組,再以item的name進(jìn)行分組

? ??Map>> collect = list.stream().collect(Collectors.groupingBy(a -> {

????????if (a.getNo() %2 ==0) {

????????????return "偶數(shù)組";

? ?????}else return "奇數(shù)組";

? ?}, Collectors.groupingBy(Item::getName)));?


把收集器的結(jié)果轉(zhuǎn)換為另一種類型 groupingBy(Function?classifier,Supplier?mapFactory,Collector?downstream)

?groupingBy(Function?classifier,Supplier mapFactory,Collector?downstream)

?使用示例:將str=‘a(chǎn)aabbbccdd’進(jìn)行分組

TreeMap result = Arrays.stream(str.split(""))

.sorted()

.collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()));???????????

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 分類函數(shù)(Function)收集器 (Supliter)? 轉(zhuǎn)換函數(shù)(Collector)


分區(qū)partitioningBy

? ??分區(qū)是分組的特殊情況:由一個謂詞(返回一個布爾值的函數(shù))作為分類函數(shù),它稱分區(qū)函數(shù)。分區(qū)函數(shù)返回一個布爾值,這意味著得到的分組Map的鍵類型是Boolean,于是它最多可以分為兩組——true是一組,false是一組.

? ? 示例:按照Item.No是否為偶數(shù)進(jìn)行分區(qū)

????list.stream().collect(Collectors.partitioningBy(a -> {

?????????if (a.getNo() %2 ==0) {

? ? `????????return true;

????? ? }else return false;

????}));


4.并行流

? ? 介紹:通過對收集源調(diào)用parallelStream方法來把集合轉(zhuǎn)換為并行流(內(nèi)部通過fork/join)。并行流就是一個把內(nèi)容分成多個數(shù)據(jù)塊,并用不同的線程分別處理每個數(shù)據(jù)塊的流。

? ? 下面通過給定N生成1到給定值N的累積值

改造前
改造后

????配置并行流使用的線程池

? ??????并行流用的線程是從哪兒來的?有多少個?怎么自定義這個過程呢?

? ??????并行流內(nèi)部使用了默認(rèn)的ForkJoinPool,它默認(rèn)的線程數(shù)量就是你的處理器數(shù)量, 這個值是由Runtime.getRuntime().available-Processors()得到的。

? ? ? ?可以通過系統(tǒng)屬性 java.util.concurrent.ForkJoinPool.common.parallelism來改變線程池大小? ? ? ? ? ? ?如:System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12");

? ??使用并行流不能發(fā)生數(shù)據(jù)爭用的情況,如改變了某些共享狀態(tài)


錯誤的使用并行流

5.fork/join(分支/合并框架)

? ??分支/合并框架的目的是以遞歸方式將可以并行的任務(wù)拆分成更小的任務(wù),然后將每個子任務(wù)的結(jié)果合并起來生成整體結(jié)果。它是ExecutorService接口的一個實(shí)現(xiàn),它把子任務(wù)分配給線程池(稱為ForkJoinPool)中的工作線程。

? ? 使用fork/join框架必須創(chuàng)建要RecursiveTask<R>的一個子類,其中R是并行化任務(wù)(以及所有子任務(wù))產(chǎn)生的結(jié)果類型,或者如果任務(wù)不返回結(jié)果,則是RecursiveAction類型(當(dāng)然它可能會更新其他非局部機(jī)構(gòu))。要定義RecursiveTask,只需實(shí)現(xiàn)它唯一的抽象方法compute。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Int Double Long 設(shè)置特定的stream類型, 提高性能,增加特定的函數(shù) 無存儲。stream不是一...
    patrick002閱讀 1,321評論 0 0
  • 轉(zhuǎn)自: Java 8 中的 Streams API 詳解 為什么需要 Stream Stream 作為 Java ...
    普度眾生的面癱青年閱讀 2,975評論 0 11
  • Stream 允許你以聲明性方式處理數(shù)據(jù)集合,流還可以透明地并行處理,你就無需寫任何多線程代碼了。和迭代器類似,流...
    PawsUp閱讀 921評論 0 3
  • 優(yōu)效學(xué)院,大數(shù)據(jù),人工智能,Java,架構(gòu)在線教育 文末領(lǐng)取資料 Java 8 發(fā)布至今也已經(jīng)好幾年過去,如今 J...
    IT科技怪閱讀 328評論 0 0
  • 什么是Java8 Stream,為什么需要Stream? Stream是Java8一大亮點(diǎn),它與 java.io ...
    消失er閱讀 1,603評論 3 22

友情鏈接更多精彩內(nèi)容