Java8 lambda表达式forEach不能提前终止循环的3种解决方案

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()方法实现

写在最后

  哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!

 相关推荐:

posted @ 2020-12-29 17:51  Marydon  阅读(11068)  评论(0编辑  收藏  举报