从Java 8stream中断或返回forEach?

当对Iterable使用外部迭代时 ,我们使用break或者从增强的for-each循环return

 for (SomeObject obj : someObjects) { if (some_condition_met) { break; // or return obj } } 

我们如何使用Java 8 lambdaexpression式中的内部迭代breakreturn

 someObjects.forEach(obj -> { //what to do here? }) 

如果你需要这个,你不应该使用forEach ,而应该使用其他方法之一。 哪一个取决于你的目标是什么。

例如,如果这个循环的目标是find匹配某个谓词的第一个元素:

 Optional<SomeObject> result = someObjects.stream().filter(obj -> some_condition_met).findFirst(); 

(注意:这不会迭代整个集合,因为stream被懒惰地评估 – 它会停在匹配条件的第一个对象上)。

如果您只想知道集合中是否存在条件为true的元素,则可以使用anyMatch

 boolean result = someObjects.stream().anyMatch(obj -> some_condition_met); 

forEach() 可能的。 解决scheme不好,但它可能的。

警告 :您不应该将其用于控制​​业务逻辑,而是纯粹用于处理执行forEach()期间发生的exception情况。 如资源突然停止访问,其中一个被处理的对象违反合同(例如,合同中说,stream中的所有元素不能为null但突然意外地其中一个为null )等等。

根据Iterable.forEach()的文档:

Iterable每个元素执行给定的操作, 直到处理所有元素,或者操作引发exception …将由操作引发的exception中继给调用者。

所以你抛出一个exception,将立即打破内部循环。

代码将是这样的 – 我不能说我喜欢它,但它的作品。 您创build了自己的类BreakException ,它扩展了RuntimeException

 try { someObjects.forEach(obj -> { // some useful code here if(some_exceptional_condition_met) { throw new BreakException(); } } } catch (BreakException e) { // here you know that your condition has been met at least once } 

请注意, try...catch 不在 lambdaexpression式周围,而是围绕整个forEach()方法。 为了使其更清晰可见,请参阅下面的代码转录,以更清晰地显示它:

 Consumer<? super SomeObject> action = obj -> { // some useful code here if(some_exceptional_condition_met) { throw new BreakException(); } }); try { someObjects.forEach(action); } catch (BreakException e) { // here you know that your condition has been met at least once } 

下面你可以find我在一个项目中使用的解决scheme。 相反forEach只使用allMatch

 someObjects.allMatch(obj -> { return !some_condition_met; }); 

要么你需要使用一个方法,使用谓词表示是否继续(所以它有中断), 或者你需要抛出一个exception – 这当然是一个非常丑陋的方法。

所以你可以写这样的forEachConditional方法:

 public static <T> void forEachConditional(Iterable<T> source, Predicate<T> action) { for (T item : source) { if (!action.test(item)) { break; } } } 

而不是Predicate<T> ,你可能想要用相同的一般方法定义你自己的函数接口(东西取一个T并返回一个bool ),但是用更清楚地表示期望的名称 – Predicate<T>在这里并不理想。

lambda中的返回等于for-each中的继续,但没有与break相等的效果。 你可以做一个返回继续:

 someObjects.forEach(obj -> { if (some_condition_met) { return; } }) 

你可以使用java8 + rxjava 。

 //import java.util.stream.IntStream; //import rx.Observable; IntStream intStream = IntStream.range(1,10000000); Observable.from(() -> intStream.iterator()) .takeWhile(n -> n < 10) .forEach(n-> System.out.println(n)); 

为了在并行操作中获得最大性能,可以使用类似于findFirst()的findAny ()。

 Optional<SomeObject> result = someObjects.stream().filter(obj -> some_condition_met).findAny(); 

但是,如果需要稳定的结果,请使用findFirst()。

还要注意匹配的模式(anyMatch()/ allMatch)只会返回布尔值,不会得到匹配的对象。

我已经完成了这样的事情

  private void doSomething() { List<Action> actions = actionRepository.findAll(); boolean actionHasFormFields = actions.stream().anyMatch(actionHasMyFieldsPredicate()); if (actionHasFormFields){ context.addError(someError); } } } private Predicate<Action> actionHasMyFieldsPredicate(){ return action -> action.getMyField1() != null; } 

你可以使用peek(..)和anyMatch(..)的混合来实现。

用你的例子:

 someObjects.stream().peek(obj -> { <your code here> }).anyMatch(obj -> !<some_condition_met>); 

或者只写一个通用的util方法:

 public static <T> void streamWhile(Stream<T> stream, Predicate<? super T> predicate, Consumer<? super T> consumer) { stream.peek(consumer).anyMatch(predicate.negate()); } 

然后使用它,像这样:

 streamWhile(someObjects.stream(), obj -> <some_condition_met>, obj -> { <your code here> }); 

用JDK提供的API来做这件事并不容易。 但是我们可以通过AbacusUtil来完成 。 这里是示例代码:

 Stream.of("a", "b", "c", "d", "e").peek(N::println) // .forEach("", (r, e) -> r + e, (e, r) -> e.equals("c")); // print: abc 

披露:我是AbacusUtil的开发者。