成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Java Stream的基本概念以及創(chuàng)建方法

瀏覽:36日期:2022-08-25 18:40:04

前言

相信很多人(包括我自己),在很長(zhǎng)一段時(shí)間內(nèi)雖然使用了 JDK 1.8 ,卻從來(lái)沒(méi)有使用過(guò)自1.8開(kāi)始增加的 Stream 這一強(qiáng)大使用的新特性,本文則將先從如何創(chuàng)建 Stream 開(kāi)始,逐步去學(xué)會(huì) Stream 的使用。本文不會(huì)涉及對(duì)流中數(shù)據(jù)的操作,而只討論創(chuàng)建流的幾種方法,以及一些基礎(chǔ)概念,關(guān)于流的實(shí)用操作將會(huì)在后續(xù)文章中一一介紹。

Stream 與 Collection 的區(qū)別

1.用途與關(guān)注點(diǎn)不同

Collection 主要關(guān)注于對(duì)象的存儲(chǔ)方面,通過(guò)使用 List 、 Map、Set等等數(shù)據(jù)結(jié)構(gòu),讓數(shù)據(jù)被更好的組織起來(lái),以便于使用。而 Stream 則關(guān)注于對(duì)象的操作方面,包含reduce、map、filter等等實(shí)用的操作。

2.流是懶搜索(Laziness-seeking)的

先看一個(gè)例子,考慮一下代碼:

Random random = new Random(29);random.ints() .filter(v -> v > 5 && v < 31) .limit(3) .forEach(System.out::println);// output:// 21// 22// 28

代碼首先創(chuàng)建了一個(gè)隨機(jī)整數(shù)流,然后過(guò)濾得到其中在(5, 31)范圍內(nèi)的數(shù),最終得到其中的3個(gè)數(shù)并輸出,這里創(chuàng)建的流就是3中所說(shuō)的無(wú)限流,而流在執(zhí)行的過(guò)程中一旦得到一個(gè)滿(mǎn)足條件的整數(shù)就會(huì)加到結(jié)果序列中,并且開(kāi)始進(jìn)行下一輪的搜索,直到找到3個(gè)滿(mǎn)足的整數(shù)為止。流只會(huì)完成所給任務(wù)(找到3個(gè)滿(mǎn)足指定范圍的整數(shù)并輸出),不會(huì)有額外的操作。

3.流的大小可以是無(wú)限的

盡管 Collection 的數(shù)據(jù)量也可以動(dòng)態(tài)擴(kuò)展改變,但由于計(jì)算機(jī)內(nèi)存是有限的,所以其數(shù)據(jù)量大小始終可以看成只能為有限的大小。但 Stream 則不同,由于流是懶加載的,所以當(dāng)使用limit類(lèi)似的短路操作時(shí),就可以利用特性2的原因去接收一個(gè)無(wú)限流。

4.流操作不存在副作用

和 Collection 中的某些操作,例如remove會(huì)刪除集合中的元素不同,流不會(huì)修改生成流的原有集合中的數(shù)據(jù),例如使用filter時(shí),會(huì)產(chǎn)生一個(gè)經(jīng)過(guò)元素過(guò)濾后的新流,而不會(huì)修改原集合中的數(shù)據(jù)。

5.流屬于消耗品(Consumable)

不同與 Collection 沒(méi)有訪問(wèn)次數(shù)與使用的限制,一個(gè)流在其生命周期中只能被執(zhí)行一次,當(dāng)執(zhí)行了終端操作(terminal operation,在之后的文章中會(huì)具體介紹)后,即使沒(méi)有將流關(guān)閉,例如上述代碼中的forEach,也無(wú)法再次訪問(wèn)了(類(lèi)似迭代器),如下代碼所示,想要再操作,必須重新創(chuàng)建一個(gè)流。

IntStream stream = new Random(29).ints();stream.filter(v -> v > 5 && v < 31) .limit(3) .forEach(System.out::println);// 當(dāng)執(zhí)行了終端操作后再使用,就會(huì)出現(xiàn)一下異常提示信息// java.lang.IllegalStateException: stream has already been operated upon or closedstream.forEach(System.out::println);

創(chuàng)建流

流可以通過(guò)很多種方式被創(chuàng)建,下面進(jìn)行一一介紹:

1.Collection 家族創(chuàng)建的方式

對(duì)于實(shí)現(xiàn)了Collection 接口的類(lèi),都可以通過(guò)stream()和parallelStream()創(chuàng)建對(duì)應(yīng)流,如下代碼所示:

List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));// 創(chuàng)建一個(gè)普通的流Stream<Integer> stream = list.stream();// 創(chuàng)建一個(gè)并行流Stream<Integer> parallelStream = list.parallelStream();

2.數(shù)組家族創(chuàng)建的方式

對(duì)于數(shù)組類(lèi)型的元素,都可以使用Arrays類(lèi)的stream()創(chuàng)建對(duì)應(yīng)的流,如果想獲得并行流則需要使用parallel()方法,如下所示:

IntStream stream = Arrays.stream(new int[]{1, 2, 3});// 生成流對(duì)應(yīng)的并行流IntStream parallelStream = stream.parallel();

3.Stream家族的工廠方法

通過(guò)工廠方法來(lái)創(chuàng)建流的方式比較多,可以通過(guò)empty、of、concat、generate、iterate、range、rangeClosed以及builder等方法創(chuàng)建流,下面就通過(guò)代碼樣例來(lái)一一介紹:

// 產(chǎn)生一個(gè)不包含任何元素的流Stream<Object> stream1 = Stream.empty();// 由給定元素所生成的流Stream<Integer> stream2 = Stream.of(1, 2, 3);// 合并兩個(gè)流產(chǎn)生一個(gè)新的流Stream<Object> stream3 = Stream.concat(stream1, stream2);// 創(chuàng)建一個(gè)<無(wú)限流>,流中的數(shù)據(jù)是通過(guò)調(diào)用所傳函數(shù)產(chǎn)生的Stream<Double> stream4 = Stream.generate(Math::random);// 創(chuàng)建一個(gè)<無(wú)限流>,流中的數(shù)據(jù)由第一個(gè)參數(shù)、將// 第一個(gè)參數(shù)作為函數(shù)參數(shù)調(diào)用產(chǎn)生的值以及不斷將// 函數(shù)調(diào)用得到的值作為參數(shù)繼續(xù)調(diào)用所組成,// 例如下面會(huì)生成1,2,3....的整數(shù)流Stream<Integer> stream5 = Stream.iterate(1, v -> v + 1);// 創(chuàng)建范圍為[1, 5)組成的整數(shù)流IntStream stream6 = IntStream.range(1, 5);// 創(chuàng)建范圍為[1, 5]組成的整數(shù)流IntStream stream7 = IntStream.rangeClosed(1, 5);// 通過(guò)流的建造者模式創(chuàng)建流Stream.Builder<Integer> builder = Stream.builder();for (int i = 0; i < 10; i++) { // add 與 accept 方法均可將元素添加到流中 // 區(qū)別是 add 無(wú)返回值, accept 會(huì)返回當(dāng)前 builder 的 this 對(duì)象 // 底層 add 方法也是調(diào)用了 accept 然后返回 this // 因此對(duì)于 add 方法可以進(jìn)行鏈?zhǔn)秸{(diào)用 builder.add(i); builder.accept(i);}Stream<Integer> stream8 = builder.build();

4.IO/NIO家族中的方法

除了兩種獲取lines生成的流外,其它幾種方式都很少使用,這一部分了解即可。

try { String dir = System.getProperty('user.dir'); // 以下兩種方法均是獲取文件中行數(shù)據(jù)組成的流 Stream<String> stream1 = new BufferedReader(new FileReader(dir + 'demo.txt')).lines(); Stream<String> stream2 = Files.lines(Paths.get(dir + 'demo.txt')); // 獲取指定路徑下所有文件/文件夾的路徑組成的流 Stream<Path> stream3 = Files.list(Paths.get('d:temp')); // 獲取指定路徑下以及指定最深文件層級(jí)內(nèi)(在這里為2)且滿(mǎn)足函數(shù)條件的所有文件/文件夾的路徑組成的流 Stream<Path> stream4 = Files.find( Paths.get('d:temp'), 1, (path, basicFileAttributes) -> path.isAbsolute()); // 獲取指定路徑下以及指定最深文件層級(jí)內(nèi)(在這里為2)所有文件/文件夾的路徑組成的流 Stream<Path> stream5 = Files.walk(Paths.get('d:temp'), 2);} catch (IOException e) { e.printStackTrace();}

5.Random 獲取流的方式

由于直接使用 Random 類(lèi)生成隨機(jī)數(shù)無(wú)限流,均為基本數(shù)據(jù)類(lèi)型組成的流,因此通常還需要使用boxed方法進(jìn)行裝箱(以前凡是生成的為IntStream,DoubleStream,LongStream均同此),以便可以使用更加豐富的特性。

Random random = new Random();// 以下三種方式得到的均是隨機(jī)數(shù)組成的<無(wú)限流>IntStream stream1 = random.ints();DoubleStream stream2 = random.doubles();LongStream stream3 = random.longs();Stream<Integer> boxedStream = stream1.boxed();

下面就先舉一個(gè)具體的實(shí)用的例子,在之后的文章中會(huì)詳細(xì)介紹一些實(shí)用操作,這里可以先做了解:

// 對(duì)數(shù)組元素進(jìn)行倒序排序// 如果不進(jìn)行裝箱(boxed)處理,則只能使用默認(rèn)的升序排序方法// 通過(guò)裝箱,則可以通過(guò)自定義比較器,實(shí)現(xiàn)更加多樣的排序int[] arr = {1, 5, 4, 6, 3, 9, 4, 5, 6, 4};int[] reverseArr = Arrays.stream(arr) .boxed() .sorted(Comparator.reverseOrder()) .mapToInt(Integer::valueOf) .toArray();// output: [9, 6, 6, 5, 5, 4, 4, 4, 3, 1]System.out.println(Arrays.toString(reverseArr));

6.其它可以生成流的類(lèi)

除了以上介紹的幾個(gè)主要可以生成流的類(lèi)之外,還有一些其它不太常見(jiàn)的可以流的類(lèi),下面是部分代碼展示:

String s = '1,2,3,4,5,6,7';// 由分割后的字符串組成的流// 在這里就是'1', '2', '3', '4', '5', '6', '7'組成的流Stream<String> stream1 = Pattern.compile(',').splitAsStream(s);BitSet bitSet = new BitSet();for (int i = 0; i < 10; i++) { if (i % 2 == 0) { bitSet.set(i); }}// 由 bitset 中被設(shè)置為 true 的位下標(biāo)所組成的流// 在這里就是0, 2, 4, 6, 8IntStream stream2 = bitSet.stream();try { String dir = System.getProperty('user.dir'); JarFile jarFile = new JarFile(dir + 'demo.jar'); // 由指定 jar 包中所有文件及文件夾的 JarEntry 對(duì)象所組形成的流 Stream<JarEntry> stream3 = jarFile.stream();} catch (IOException e) { e.printStackTrace();}

此外還可以通過(guò) StreamSupport工具類(lèi)進(jìn)行產(chǎn)生和操作流,由于本文包括之后的文章主要是為了入門(mén)和先簡(jiǎn)單上手,所以這里不做詳細(xì)討論,感興趣的可以自己進(jìn)行查閱資料。

總結(jié)

本文簡(jiǎn)單介紹了 Stream 這個(gè)自1.8開(kāi)始引入的新特性,然后簡(jiǎn)單介紹了一些基本概念和流的創(chuàng)建方式,在接下來(lái)的文章中還會(huì)介紹流的一些實(shí)用操作,希望能和大家一起學(xué)會(huì)使用 Stream 這個(gè)實(shí)用的特性,當(dāng)然本文也難免有錯(cuò)誤之處,希望得到各位的指正。

以上就是Java Stream的基本概念以及創(chuàng)建方法的詳細(xì)內(nèi)容,更多關(guān)于JAVA Stream的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章: