Java8 lambda表達式forEach不能提前終止循環的兩種解決方案


1.情景展示

  如上圖所示,我們想要終止for循環,使用return。

  執行結果如下: 

 

  我們可以看到,只有趙六沒被打印出來,后續的數組元素依舊被執行了。

  也就是說,關鍵字"return",在這里執行的效果相當於普通for循環里的關鍵詞continue"。

2.原因分析

  我們知道,在普通for循環里面,想要提前結束(終止)循環體使用"break";

  結束本輪循環,進行下一輪循環使用"continue";

  另外,在普通for里,如果使用"return",不僅強制結束for循環體,還會提前結束包含這個循環體的整個方法。

  而在Java8中的forEach()中,"break"或"continue"是不被允許使用的,而return的意思也不是原來return代表的含義了。

  我們來看看源碼:

  forEach(),說到底是一個方法,而不是循環體,結束一個方法的執行用什么? 當然是return啦;

  java8的forEach()和JavaScript的forEach()用法是何其的相似,感興趣的可以去了解下(在文末)。

  Java不是萬能的,不要再吐槽它垃圾了。

3.解決方案

  方案一:使用原始的foreach循環

  使用過eclipse的老鐵們應該知道,當我們輸入:foreach,再按快捷鍵:Alt+/,就會出現foreach的代碼提示。

  如上圖所示,這種格式的for循環才是真正意義上的foreach循環。

  在idea中輸入,按照上述操作是不會有任何代碼提示的,那如何才能在idea中,調出來呢?

  for循環可以提前終止。 

  方式一:break

  方式二:return(不推薦使用)

  方案二:拋出異常 

  我們知道,要想結束一個方法的執行,正常的邏輯是:使用return;

  但是,在實際運行中,往往有很多不突發情況導致代碼提前終止,比如:空指針異常,其實,我們也可以通過拋出假異常的方式來達到終止forEach()方法的目的。

  如果覺得這種方式不友好,可以再包裝一層。   

 

  這樣,就完美了。

  這里,需要注意的一點是:要確保你forEach()方法體內不能有其它代碼可能會拋出的異常與自己手動拋出並捕獲的異常一樣;

  否則,當真正該因異常導致代碼終止的時候,因為咱們手動捕獲了並且沒做任何處理,豈不是搬起石頭砸自己的腳嗎?

2021年11月12日10:47:07

方案三:filter()

通過filter()篩選出符合條件的數據。

List<Integer> list = new ArrayList<>(4);
list.add(111);
list.add(222);
list.add(333);
list.add(444);

// 通過filter拿到符合條件的數據
list.stream().filter(num -> num == 111 || num == 222).forEach(num -> System.out.println(num));

注意:多個符合條件可以使用或||來疊加,不能再次使用filter()進行過濾。

代表的含義是:

從list中拿到數據為111的元素(即前兩步),從前兩步得到的結果,去拿數據為222的元素(即第三步);

這當然會拿不到,所以第4步進行遍歷的時候就是空。

雖然這樣方法比較笨,但是,也算是一種辦法,盡管在這里不太適用,依舊是一種解決思路,在后續中難免會用到。

4.拓展

在遍歷的時候,如果要使用java8的lambda表達式進行遍歷的時候,建議通過流:list.stream().forEach()來進行;

而不是僅僅遍歷:list.forEach()。

流基本知識普及(以下內容非原創)

什么是流?
Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator;
原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操作;
高級版本的 Stream,用戶只要給出需要對其包含的元素執行什么操作,比如 “過濾掉長度大於 10 的字符串”、“獲取每個字符串的首字母”等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。

Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次后即用盡了,就好比流水從面前流過,一去不復返。

而和迭代器又不同的是:Stream 可以並行化操作;
迭代器只能命令式地、串行化操作。顧名思義,當使用串行方式去遍歷時,每個 item 讀完后再讀下一個 item。
而使用並行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然后將結果一起輸出。Stream 的並行操作依賴於 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。

Java 的並行 API 演變歷程基本如下:
1.0-1.4 中的 java.lang.Thread
5.0 中的 java.util.concurrent
6.0 中的 Phasers 等
7.0 中的 Fork/Join 框架
8.0 中的 Lambda
Stream 的另外一大特點是,數據源本身可以是無限的。

流的構成:
當我們使用一個流的時候,通常包括三個基本步驟:

獲取一個數據源(source)→ 數據轉換→執行操作獲取想要的結果,每次轉換原有 Stream 對象不改變,返回一個新的 Stream 對象(可以有多次轉換),這就允許對其操作可以像鏈條一樣排列,變成一個管道。

濃縮一下就是:

流可以無限大,不受原集合大小限制;

將list轉換成流,流將和原有集合沒有關聯;

流可以並發遍歷,大大提高遍歷速度。

將list流化之后,如何變回list?

通過collect()方法實現

寫在最后

  哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!

 相關推薦:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM