JavaStream流式編程常見(jiàn)的坑有哪些

本篇內(nèi)容介紹了“Java Stream流式編程常見(jiàn)的坑有哪些”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

創(chuàng)新互聯(lián)建站服務(wù)項(xiàng)目包括扶余網(wǎng)站建設(shè)、扶余網(wǎng)站制作、扶余網(wǎng)頁(yè)制作以及扶余網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃等。多年來(lái),我們專(zhuān)注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,扶余網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶(hù)以成都為中心已經(jīng)輻射到扶余省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶(hù)的支持與信任!

1. Stream是什么?

Stream是Java8新增的一個(gè)接口,允許以聲明性方式處理數(shù)據(jù)集合。Stream不是一個(gè)集合類(lèi)型不保存數(shù)據(jù),可以把它看作是遍歷數(shù)據(jù)集合的高級(jí)迭代器(Iterator)。

Stream操作可以像Builder一樣逐步疊加,形成一條流水線(xiàn)。流水線(xiàn)一般由數(shù)據(jù)源+零或者多個(gè)中間操作+一個(gè)終端操作所構(gòu)成。中間操作可以將流轉(zhuǎn)換成另外一個(gè)流,比如使用filter過(guò)濾元素,使用map映射提取值。

Stream與lambda表達(dá)式密不可分,本文默認(rèn)你已經(jīng)掌握了lambda基礎(chǔ)知識(shí)。

2. Stream的特點(diǎn)

  • 只能遍歷(消費(fèi))一次。Stream實(shí)例只能遍歷一次,終端操作后一次遍歷就結(jié)束,再次遍歷需要重新生成實(shí)例,這一點(diǎn)類(lèi)似于Iterator迭代器。

  • 保護(hù)數(shù)據(jù)源。對(duì)Stream中任何元素的修改都不會(huì)導(dǎo)致數(shù)據(jù)源被修改,比如過(guò)濾刪除流中的一個(gè)元素,再次遍歷該數(shù)據(jù)源依然可以獲取該元素。

  • 懶。filter, map 操作串聯(lián)起來(lái)形成一系列中間運(yùn)算,如果沒(méi)有一個(gè)終端操作(如collect)這些中間運(yùn)算永遠(yuǎn)也不會(huì)被執(zhí)行。

3. 創(chuàng)建Stream實(shí)例的方法

(1)使用指定值創(chuàng)建Stream實(shí)例

// of為Stream的靜態(tài)方法 Stream<String> strStream = Stream.of("hello", "java8", "stream"); // 或者使用基本類(lèi)型流 IntStream intStream = IntStream.of(1, 2, 3); 復(fù)制代碼

(2)使用集合創(chuàng)建Stream實(shí)例(常用方式)

// 使用guava庫(kù),初始化一個(gè)不可變的list對(duì)象 ImmutableList<Integer> integers = ImmutableList.of(1, 2, 3); // List接口繼承Collection接口,java8在Collection接口中添加了stream方法 Stream<Integer> stream = integers.stream(); 復(fù)制代碼

(3)使用數(shù)組創(chuàng)建Stream實(shí)例

// 初始化一個(gè)數(shù)組 Integer[] array = {1, 2, 3}; // 使用Arrays的靜態(tài)方法stream Stream<Integer> stream = Arrays.stream(array); 復(fù)制代碼

(4)使用生成器創(chuàng)建Stream實(shí)例

// 隨機(jī)生成100個(gè)整數(shù) Random random = new Random(); // 加上limit否則就是無(wú)限流了 Stream<Integer> stream = Stream.generate(random::nextInt).limit(100); 復(fù)制代碼

(5)使用迭代器創(chuàng)建Stream實(shí)例

// 生成100個(gè)奇數(shù),加上limit否則就是無(wú)限流了 Stream<Integer> stream = Stream.iterate(1, n -> n + 2).limit(100); stream.forEach(System.out::println); 復(fù)制代碼

(6)使用IO接口創(chuàng)建Stream實(shí)例

// 獲取指定路徑下文件信息,list方法返回Stream類(lèi)型 Stream<Path> pathStream = Files.list(Paths.get("/")); 復(fù)制代碼

4. Stream常用操作

Stream接口中定義了很多操作,大致可以分為兩大類(lèi),一類(lèi)是中間操作,另一類(lèi)是終端操作;

Java Stream流式編程常見(jiàn)的坑有哪些

(1)中間操作

中間操作會(huì)返回另外一個(gè)流,多個(gè)中間操作可以連接起來(lái)形成一個(gè)查詢(xún)。

中間操作有惰性,如果流上沒(méi)有一個(gè)終端操作,那么中間操作是不會(huì)做任何處理的。

下面介紹常用的中間操作:

map操作

map是將輸入流中每一個(gè)元素映射為另一個(gè)元素形成輸出流。

// 初始化一個(gè)不可變字符串 List<String> words = ImmutableList.of("hello", "java8", "stream"); // 計(jì)算列表中每個(gè)單詞的長(zhǎng)度 List<Integer> list = words.stream()         .map(String::length)         .collect(Collectors.toList()); // output: 5 5 6 list.forEach(System.out::println); 復(fù)制代碼

flatMap操作

List<String[]> list1 = words.stream()         .map(word -> word.split("-"))         .collect(Collectors.toList());          // output: [Ljava.lang.String;@59f95c5d,  //             [Ljava.lang.String;@5ccd43c2 list1.forEach(System.out::println); 復(fù)制代碼

哪里?你預(yù)期是List, 返回卻是List<String[]>, 這是因?yàn)閟plit方法返回的是String[]

這個(gè)時(shí)候你可以想到要將數(shù)組轉(zhuǎn)成stream, 于是有了第二個(gè)版本

Stream<Stream<String>> arrStream = words.stream()         .map(word -> word.split("-"))         .map(Arrays::stream);          // output: java.util.stream.ReferencePipeline$Head@2c13da15,  // java.util.stream.ReferencePipeline$Head@77556fd arrStream.forEach(System.out::println); 復(fù)制代碼

還是不對(duì),這個(gè)問(wèn)題使用flatMap扁平流可以解決,flatMap將流中每個(gè)元素取出來(lái)轉(zhuǎn)成另外一個(gè)輸出流

Stream<String> strStream = words.stream()         .map(word -> word.split("-"))         .flatMap(Arrays::stream)         .distinct(); // output: hello java8 stream strStream.forEach(System.out::println); 復(fù)制代碼

filter操作

filter接收Predicate對(duì)象,按條件過(guò)濾,符合條件的元素生成另外一個(gè)流。

// 過(guò)濾出單詞長(zhǎng)度大于5的單詞,并打印出來(lái) List<String> words = ImmutableList.of("hello", "java8", "hello", "stream"); words.stream()         .filter(word -> word.length() > 5)         .collect(Collectors.toList())         .forEach(System.out::println); // output: stream 復(fù)制代碼

(2)終端操作

終端操作將stream流轉(zhuǎn)成具體的返回值,比如List,Integer等。常見(jiàn)的終端操作有:foreach, min, max, count等。

foreach很常見(jiàn)了,下面舉一個(gè)max的例子。

// 找出最大的值 List<Integer> integers = Arrays.asList(6, 20, 19); integers.stream()         .max(Integer::compareTo)         .ifPresent(System.out::println); // output: 20 復(fù)制代碼

5. 實(shí)戰(zhàn):使用Stream重構(gòu)老代碼

假如有一個(gè)需求:過(guò)濾出年齡大于20歲并且分?jǐn)?shù)大于95的學(xué)生。

使用for循環(huán)寫(xiě)法:

private List<Student> getStudents() {     Student s1 = new Student("xiaoli", 18, 95);     Student s2 = new Student("xiaoming", 21, 100);     Student s3 = new Student("xiaohua", 19, 98);     List<Student> studentList = Lists.newArrayList();     studentList.add(s1);     studentList.add(s2);     studentList.add(s3);     return studentList; } public void refactorBefore() {     List<Student> studentList = getStudents();     // 使用臨時(shí)list     List<Student> resultList = Lists.newArrayList();     for (Student s : studentList) {         if (s.getAge() > 20 && s.getScore() > 95) {             resultList.add(s);         }     }     // output: Student{name=xiaoming, age=21, score=100}     resultList.forEach(System.out::println); } 復(fù)制代碼

使用for循環(huán)會(huì)初始化一個(gè)臨時(shí)list用來(lái)存放最終的結(jié)果,整體看起來(lái)不夠優(yōu)雅和簡(jiǎn)潔。

使用lambda和stream重構(gòu)后:

public void refactorAfter() {     List<Student> studentLists = getStudents();     // output: Student{name=xiaoming, age=21, score=100}    studentLists.stream().filter(this::filterStudents).forEach(System.out::println); } private boolean filterStudents(Student student) {     // 過(guò)濾出年齡大于20歲并且分?jǐn)?shù)大于95的學(xué)生     return student.getAge() > 20 && student.getScore() > 95; } 復(fù)制代碼

使用filter和方法引用使代碼清晰明了,也不用聲明一個(gè)臨時(shí)list,非常方便。

6. 使用Stream常見(jiàn)的誤區(qū)

(1)誤區(qū)一:重復(fù)消費(fèi)stream對(duì)象

stream對(duì)象一旦被消費(fèi),不能再次重復(fù)消費(fèi)。

List<String> strings = Arrays.asList("hello", "java8", "stream"); Stream<String> stream = strings.stream(); stream.forEach(System.out::println); // ok stream.forEach(System.out::println); // IllegalStateException 復(fù)制代碼

上述代碼執(zhí)行后報(bào)錯(cuò):

java.lang.IllegalStateException: stream has already been operated upon or closed

(2)誤區(qū)二:修改數(shù)據(jù)源

在流操作的過(guò)程中嘗試添加新的string對(duì)象,結(jié)果報(bào)錯(cuò):

List<String> strings = Arrays.asList("hello", "java8", "stream"); // expect: HELLO JAVA8 STREAM WORLD, but throw UnsupportedOperationException strings.stream()         .map(s -> {             strings.add("world");             return s.toUpperCase();         }).forEach(System.out::println); 復(fù)制代碼

注意:一定不要在操作流的過(guò)程中修改數(shù)據(jù)源。

“Java Stream流式編程常見(jiàn)的坑有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

名稱(chēng)欄目:JavaStream流式編程常見(jiàn)的坑有哪些
文章出自:http://www.muchs.cn/article8/geesip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序網(wǎng)站制作、建站公司、企業(yè)網(wǎng)站制作、服務(wù)器托管、小程序開(kāi)發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

手機(jī)網(wǎng)站建設(shè)