摘要:可以使用方法替換常規(guī)循環(huán)以上代碼的產(chǎn)出所有這些原始流都像常規(guī)對(duì)象流一樣工作,但有以下不同之處原始流使用專門的表達(dá)式,例如代替或代替。原始流支持額外的終端聚合操作,以上代碼的產(chǎn)出有時(shí)將常規(guī)對(duì)象流轉(zhuǎn)換為基本流是有用的,反之亦然。
本文提供了有關(guān)Java 8 Stream的深入概述。當(dāng)我第一次讀到的Stream API,我感到很困惑,因?yàn)樗犉饋眍愃艼ava I/O的InputStream,OutputStream。但Java 8 Stream是完全不同的東西。Streams是Monads,因此在為Java提供函數(shù)式編程方面發(fā)揮了重要作用:
在函數(shù)式編程中,monad是表示定義為步驟序列的計(jì)算的結(jié)構(gòu)。具有monad結(jié)構(gòu)的類型定義鏈操作的含義,或?qū)⒃擃愋偷暮瘮?shù)嵌套在一起。
本文詳解如何使用Java 8 Stream以及如何使用不同類型的可用流操作。您將了解處理順序以及流操作的順序如何影響運(yùn)行時(shí)性能。并對(duì)更強(qiáng)大的reduce,collect,flatMap流操作詳細(xì)介紹。
如果您還不熟悉Java 8 lambda表達(dá)式,函數(shù)接口和方法引用,那么您可能需要了解Java 8。
Stram如何工作Stream表示一系列元素,并支持不同類型的操作以對(duì)這些元素執(zhí)行計(jì)算:
Liststreams = Arrays.asList("a1", "a2", "b1", "c2", "c1"); streams .stream() .filter(s -> s.startsWith("c")) .map(String::toUpperCase) .sorted() .forEach(System.out::println);
以上代碼的產(chǎn)出:
C1 C2
Stream操作是中間操作或終端操作。中間操作返回一個(gè)流,因此我們可以鏈接多個(gè)中間操作而不使用分號(hào)。終端操作無效或返回非流結(jié)果。在上述例子中filter,map和sorted是中間操作,而forEach是一個(gè)終端的操作。有關(guān)所有可用流操作的完整列表,請(qǐng)參閱Stream Javadoc。如上例中所見的這種流操作鏈也稱為操作管道。
大多數(shù)流操作都接受某種lambda表達(dá)式參數(shù),這是一個(gè)指定操作的確切行為的功能接口。大多數(shù)這些操作必須是不受干擾和無狀態(tài)。
當(dāng)函數(shù)不修改流的基礎(chǔ)數(shù)據(jù)源時(shí),該函數(shù)是不受干擾的,例如在上面的示例中,沒有l(wèi)ambda表達(dá)式通過從集合中添加或刪除元素來修改streams。
當(dāng)操作的執(zhí)行是確定性的時(shí),函數(shù)是無狀態(tài)的,例如在上面的示例中,沒有l(wèi)ambda表達(dá)式依賴于任何可變變量或來自外部作用域的狀態(tài),其可能在執(zhí)行期間改變。
不同種類的Stream可以從各種數(shù)據(jù)源創(chuàng)建流,尤其是集合。Lists和Sets支持新的方法stream()和parallelStream()來創(chuàng)建順序流或并行流。并行流能夠在多個(gè)線程上操作,后面的部分將對(duì)此進(jìn)行介紹。我們現(xiàn)在關(guān)注的是順序流:
Arrays.asList("a1", "a2", "a3") .stream() .findFirst() .ifPresent(System.out::println);
以上代碼的產(chǎn)出:
a1
在對(duì)象列表上調(diào)用stream()方法將返回常規(guī)對(duì)象流。但是我們不必創(chuàng)建集合以便使用流,就像我們?cè)谙乱粋€(gè)代碼示例中看到的那樣:
Stream.of("a1", "a2", "a3") .findFirst() .ifPresent(System.out::println);
以上代碼的產(chǎn)出:
a1
只是用來Stream.of()從一堆對(duì)象引用創(chuàng)建一個(gè)流。
除了常規(guī)對(duì)象流之外,Java 8還附帶了特殊類型的流,用于處理原始數(shù)據(jù)類型int,long以及double。你可能已經(jīng)猜到了IntStream,LongStream,DoubleStream。
IntStreams可以使用IntStream.range()方法替換常規(guī)for循環(huán):
IntStream.range(1, 4) .forEach(System.out::println);
以上代碼的產(chǎn)出:
1 2 3
所有這些原始流都像常規(guī)對(duì)象流一樣工作,但有以下不同之處:原始流使用專門的lambda表達(dá)式,例如IntFunction代替Function或IntPredicate代替Predicate。原始流支持額外的終端聚合操作,sum(),average():
Arrays.stream(new int[] {1, 2, 3}) .map(n -> 2 * n + 1) .average() .ifPresent(System.out::println);
以上代碼的產(chǎn)出:
5.0
有時(shí)將常規(guī)對(duì)象流轉(zhuǎn)換為基本流是有用的,反之亦然。為此,對(duì)象流支持特殊的映射操作mapToInt(),mapToLong(),mapToDouble:
Stream.of("a1", "a2", "a3") .map(s -> s.substring(1)) .mapToInt(Integer::parseInt) .max() .ifPresent(System.out::println);
以上代碼的產(chǎn)出:
3
可以通過mapToObj()方式將原始流轉(zhuǎn)換為對(duì)象流:
IntStream.range(1, 4) .mapToObj(i -> "a" + i) .forEach(System.out::println);
以上代碼的產(chǎn)出:
a1 a2 a3
下面是一個(gè)組合示例:雙精度流首先映射到int流,然后映射到字符串的對(duì)象流:
Stream.of(1.0, 2.0, 3.0) .mapToInt(Double::intValue) .mapToObj(i -> "a" + i) .forEach(System.out::println);
以上代碼的產(chǎn)出:
a1 a2 a3處理過程
現(xiàn)在我們已經(jīng)學(xué)會(huì)了如何創(chuàng)建和使用不同類型的流,讓我們深入了解如何在流程下處理流操作。
中間操作的一個(gè)重要特征是懶惰。查看缺少終端操作的示例:
Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return true; });
執(zhí)行此代碼段時(shí),不會(huì)向控制臺(tái)打印任何內(nèi)容。這是因?yàn)橹挥性诖嬖诮K端操作時(shí)才執(zhí)行中間操作。
讓我們通過forEach終端操作擴(kuò)展上面的例子:
Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return true; }) .forEach(s -> System.out.println("forEach: " + s));
執(zhí)行此代碼段會(huì)在控制臺(tái)上產(chǎn)生所需的輸出:
filter: d2 forEach: d2 filter: a2 forEach: a2 filter: b1 forEach: b1 filter: b3 forEach: b3 filter: c forEach: c
結(jié)果的順序可能會(huì)令人驚訝。默認(rèn)認(rèn)為是在流的所有元素上一個(gè)接一個(gè)地水平執(zhí)行操作。但相反,每個(gè)元素都沿著鏈垂直移動(dòng)。第一個(gè)字符串“d2”通過filter,然后forEach,然后處理第二個(gè)字符串“a2”。
此行為可以減少對(duì)每個(gè)元素執(zhí)行的實(shí)際操作數(shù),如下一個(gè)示例所示:
Stream.of("d2", "a2", "b1", "b3", "c") .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .anyMatch(s -> { System.out.println("anyMatch: " + s); return s.startsWith("A"); });
代碼產(chǎn)出
map: d2 anyMatch: D2 map: a2 anyMatch: A2
一旦謂詞應(yīng)用于給定的輸入元素,anyMatch操作將返回true。這對(duì)于傳遞給“A2”的第二個(gè)元素是正確的。由于流鏈的垂直執(zhí)行,map在這種情況下映射只需執(zhí)行兩次。因此,不是映射流的所有元素,而是map盡可能少地調(diào)用。
復(fù)雜的處理過程下一個(gè)示例包括兩個(gè)map,filter中間操作和forEach終端操作。讓我們?cè)俅螜z查這些操作是如何執(zhí)行的:
Stream.of("d2", "a2", "b1", "b3", "c") .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .filter(s -> { System.out.println("filter: " + s); return s.startsWith("A"); }) .forEach(s -> System.out.println("forEach: " + s));
代碼產(chǎn)出:
map: d2 filter: D2 map: a2 filter: A2 forEach: A2 map: b1 filter: B1 map: b3 filter: B3 map: c filter: C
正如您可能已經(jīng)猜到的,對(duì)于底層集合中的每個(gè)字符串,map和filter都被調(diào)用5次,而forEach只被調(diào)用一次。
如果我們改變操作的順序,移動(dòng)filter到鏈的開頭,我們可以大大減少實(shí)際的執(zhí)行次數(shù):
Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s));
代碼產(chǎn)出:
filter: d2 filter: a2 map: a2 forEach: A2 filter: b1 filter: b3 filter: c
現(xiàn)在,map只調(diào)用一次,因此操作管道對(duì)大量輸入元素的執(zhí)行速度要快得多。在編寫復(fù)雜的方法鏈時(shí)要記住這一點(diǎn)。
讓我們通過一個(gè)sorted額外的操作來擴(kuò)展上面的例子:
Stream.of("d2", "a2", "b1", "b3", "c") .sorted((s1, s2) -> { System.out.printf("sort: %s; %s ", s1, s2); return s1.compareTo(s2); }) .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s));
排序是一種特殊的中間操作。這是一個(gè)所謂的有狀態(tài)操作,因?yàn)闉榱藢?duì)在排序期間必須維護(hù)狀態(tài)的元素集合進(jìn)行排序。
執(zhí)行此示例將導(dǎo)致以下控制臺(tái)輸出:
sort: a2; d2 sort: b1; a2 sort: b1; d2 sort: b1; a2 sort: b3; b1 sort: b3; d2 sort: c; b3 sort: c; d2 filter: a2 map: a2 forEach: A2 filter: b1 filter: b3 filter: c filter: d2
首先,對(duì)整個(gè)輸入集合執(zhí)行排序操作。換句話說,sorted是水平執(zhí)行的。因此,在這種情況下sorted,對(duì)輸入集合中的每個(gè)元素的多個(gè)組合調(diào)用八次。
我們可以通過重新排序鏈來優(yōu)化性能:
Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .sorted((s1, s2) -> { System.out.printf("sort: %s; %s ", s1, s2); return s1.compareTo(s2); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s));
代碼產(chǎn)出
filter: d2 filter: a2 filter: b1 filter: b3 filter: c map: a2 forEach: A2
在此示例sorted從未被調(diào)用過,因?yàn)閒ilter將輸入集合減少到只有一個(gè)元素。因此,對(duì)于較大的輸入集合,性能會(huì)大大提高。
重用StreamJava 8 Stream無法重用。只要您調(diào)用任何終端操作,流就會(huì)關(guān)閉:
Streamstream = Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> s.startsWith("a")); stream.anyMatch(s -> true); // ok stream.noneMatch(s -> true); // exception
在同一流上的anyMatch之后調(diào)用noneMatch會(huì)導(dǎo)致以下異常:
java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229) at java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459) at com.winterbe.java8.Streams5.test7(Streams5.java:38) at com.winterbe.java8.Streams5.main(Streams5.java:28)
為了克服這個(gè)限制,我們必須為我們想要執(zhí)行的每個(gè)終端操作創(chuàng)建一個(gè)新的流鏈,例如我們可以創(chuàng)建一個(gè)流供應(yīng)商來構(gòu)建一個(gè)新的流,其中已經(jīng)設(shè)置了所有中間操作:
Supplier> streamSupplier = () -> Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> s.startsWith("a")); streamSupplier.get().anyMatch(s -> true); // ok streamSupplier.get().noneMatch(s -> true); // ok
每次調(diào)用get()構(gòu)造一個(gè)我們保存的新流,以調(diào)用所需的終端操作。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/72892.html
摘要:接受包含四種不同操作的操作供應(yīng)商,累加器,組合器和修整器。累加器用于將每個(gè)人的大寫名稱添加到。第二種方法接受標(biāo)識(shí)值和累加器。由于累加器是并行調(diào)用的,因此需要組合器來對(duì)各個(gè)累加值求和。 Streams支持大量不同的操作。我們已經(jīng)了解了最重要的操作,如filter,map。發(fā)現(xiàn)所有其他可用的操作(參見Stream Javadoc)。我們深入研究更復(fù)雜的操作collect,flatMap,r...
摘要:上一篇我們介紹了的概念與實(shí)際的一些操作,本篇我們繼續(xù)來學(xué)習(xí)的另一個(gè)重要操作,分組與分區(qū)。注意到分組后的返回類型是,結(jié)果集中會(huì)將作為,對(duì)應(yīng)的集合作為返回。 上一篇我們介紹了Strem的概念與實(shí)際的一些操作,本篇我們繼續(xù)來學(xué)習(xí)Stream的另一個(gè)重要操作,分組與分區(qū)。我們?cè)谏弦黄榻BStream的操作時(shí),會(huì)經(jīng)常使用到Collectors這個(gè)類,這個(gè)類實(shí)際上是一個(gè)封裝了很多常用的匯聚操作的一...
摘要:本文基于環(huán)境,采用為基礎(chǔ)來構(gòu)建實(shí)時(shí)人臉檢測(cè)與識(shí)別系統(tǒng),探索人臉識(shí)別系統(tǒng)在現(xiàn)實(shí)應(yīng)用中的難點(diǎn)。對(duì)于人臉檢測(cè)方法,效果好于的方法,但是檢測(cè)力度也難以達(dá)到現(xiàn)場(chǎng)應(yīng)用標(biāo)準(zhǔn)。本文中,我們采用了基于深度學(xué)習(xí)方法的人臉檢測(cè)系統(tǒng)。 git地址:https://github.com/chenlinzho... 本文主要介紹了系統(tǒng)涉及的人臉檢測(cè)與識(shí)別的詳細(xì)方法,該系統(tǒng)基于python2.7.10/opencv...
摘要:本文基于環(huán)境,采用為基礎(chǔ)來構(gòu)建實(shí)時(shí)人臉檢測(cè)與識(shí)別系統(tǒng),探索人臉識(shí)別系統(tǒng)在現(xiàn)實(shí)應(yīng)用中的難點(diǎn)。對(duì)于人臉檢測(cè)方法,效果好于的方法,但是檢測(cè)力度也難以達(dá)到現(xiàn)場(chǎng)應(yīng)用標(biāo)準(zhǔn)。本文中,我們采用了基于深度學(xué)習(xí)方法的人臉檢測(cè)系統(tǒng)。 git地址:https://github.com/chenlinzho... 本文主要介紹了系統(tǒng)涉及的人臉檢測(cè)與識(shí)別的詳細(xì)方法,該系統(tǒng)基于python2.7.10/opencv...
摘要:爬蟲下載一最近在學(xué)習(xí)的爬蟲,并且玩的不亦說乎,因此寫個(gè)博客,記錄并分享一下。 Python3爬蟲下載pdf(一) 最近在學(xué)習(xí)python的爬蟲,并且玩的不亦說乎,因此寫個(gè)博客,記錄并分享一下。 需下載以下模塊 bs4 模塊 requests 模塊 一、源碼 功能:下載指定url內(nèi)的所有的pdf 語法:將含有pdf的url放到腳本后面執(zhí)行就可以了 from bs4 import...
閱讀 2650·2021-09-08 09:45
閱讀 3540·2021-09-08 09:45
閱讀 3191·2019-08-30 15:54
閱讀 3442·2019-08-26 13:54
閱讀 1513·2019-08-26 13:26
閱讀 1463·2019-08-26 13:23
閱讀 999·2019-08-23 17:57
閱讀 2290·2019-08-23 17:14