从Java 8流中断或返回每个?

Tap*_*ose 275 java foreach lambda java-8

使用外部迭代时,Iterable我们使用breakreturn来自增强型for-each循环:

for (SomeObject obj : someObjects) {
   if (some_condition_met) {
      break; // or return obj
   }
}
Run Code Online (Sandbox Code Playgroud)

我们如何在Java 8 lambda表达式中使用breakreturn使用内部迭代,如:

someObjects.forEach(obj -> {
   //what to do here?
})
Run Code Online (Sandbox Code Playgroud)

Jes*_*per 342

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

例如,如果此循环的目标是找到与某个谓词匹配的第一个元素:

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findFirst();
Run Code Online (Sandbox Code Playgroud)

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

如果您只想知道条件为真的集合中是否有元素,您可以使用anyMatch:

boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);
Run Code Online (Sandbox Code Playgroud)

  • 如果*找到一个对象是目标,那么这种方法很有效,但是这个目标通常由`findSomething`方法的`return`语句提供.`break`通常与*-kind操作相关联. (5认同)
  • 当目标是正确实施取消行为时呢?当我们注意到用户请求取消时,我们能做的最好的事情是在forEach lambda中抛出运行时异常吗? (3认同)

Hon*_*dek 51

可能的Iterable.forEach()(但不可靠Stream.forEach()).解决方案并不好,但它可能的.

警告:您不应该使用它来控制业务逻辑,而是纯粹用于处理执行期间发生的异常情况forEach().例如资源突然停止被访问,其中一个处理过的对象违反了合同(例如,合同说流中的所有元素都不能null突然而且意外地是其中之一null)等.

根据以下文件Iterable.forEach():

对每个元素执行给定的操作,Iterable 直到处理完所有元素或者操作抛出异常 ... 操作抛出的异常被中继到调用者.

所以你抛出一个会立即破坏内部循环的异常.

代码将是这样的 - 我不能说我喜欢它,但它的工作原理.您创建自己的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
}
Run Code Online (Sandbox Code Playgroud)

请注意,try...catch不是围绕lambda表达式,而是围绕整个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
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为这是一种不好的做法,不应被视为解决问题的方法.它很危险,因为它可能会误导初学者.根据Effective Java第2版,第9章,第57项:"仅对异常条件使用例外".此外,"使用运行时异常来指示编程错误".最后,我强烈建议任何考虑这个解决方案的人都要研究@Jesper解决方案. (32认同)
  • @LouisF.我明确地说"我不能说我喜欢但它有效".OP问"如何打破forEach()",这是一个答案.我完全同意这不应该用于控制业务逻辑.但是我可以想象一些有用的用例,比如在forEach()左右突然无法使用的资源连接,使用异常并不是坏习惯.我在答案中添加了一个段落,以便明确. (12认同)
  • 请注意[Stream.forEach`](https://docs.oracle.com/javase/10/docs/api/java/util/stream/Stream.html#forEach%28java.util.function.Consumer%29) *不会*为将异常中继到调用者提供相同的有力保证,因此,不能保证抛出异常对Stream.forEach来说是这样的。 (2认同)

Ane*_*ran 35

lambda中的返回等于for-each中的continue,但没有相当于break.您可以返回继续:

someObjects.forEach(obj -> {
   if (some_condition_met) {
      return;
   }
})
Run Code Online (Sandbox Code Playgroud)

  • 换句话说,这不是“从Java 8流forEach中断或返回”,这是实际的问题 (3认同)
  • 不过,这仍然会通过源流“拉”记录,如果您要分页某种远程数据集,这会很糟糕。 (3认同)
  • 满足一般要求的不错且惯用的解决方案。接受的答案推断出要求。 (2认同)

Jul*_*les 21

您可以在下面找到我在项目中使用的解决方案.而forEach只是使用allMatch:

someObjects.allMatch(obj -> {
    return !some_condition_met;
});
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 11

无论您需要使用它使用谓词指示是否继续去(因此它具有跳转代替)的方法,或者你需要抛出一个异常-这是一个非常丑陋的做法,当然.

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

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

而不是Predicate<T>,您可能希望使用相同的通用方法定义自己的功能接口(采用T并返回a的东西bool),但使用更清楚地表示期望的名称 - Predicate<T>在这里并不理想.

  • 这是经典的`takeWhile`操作,这个问题只是其中一个证明了它在Streams API中缺乏多少的问题. (3认同)
  • @Marko:takeWhile感觉更像是一个产生物品的操作,而不是对每个物品执行动作.当然,在.NET中的LINQ中,将TakeWhile与带有副作用的动作一起使用会很糟糕. (2认同)

frh*_*ack 8

您可以使用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));
Run Code Online (Sandbox Code Playgroud)

  • Java 9将为流上的takeWhile操作提供支持. (8认同)

Kan*_*mar 6

为了在并行操作中获得最佳性能,请使用findAny(),它与findFirst()相似。

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findAny();
Run Code Online (Sandbox Code Playgroud)

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

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


小智 6

public static void main(String[] args) {
    List<String> list = Arrays.asList("one", "two", "three", "seven", "nine");
    AtomicBoolean yes = new AtomicBoolean(true);
    list.stream().takeWhile(value -> yes.get()).forEach(value -> {
        System.out.println("prior cond" + value);
        if (value.equals("two")) {
            System.out.println(value);
            yes.set(false);
        }

    });
    //System.out.println("Hello World");
}
Run Code Online (Sandbox Code Playgroud)


use*_*739 5

使用Java 9+更新takeWhile

MutableBoolean ongoing = MutableBoolean.of(true);
someobjects.stream()...takeWhile(t -> ongoing.value()).forEach(t -> {
    // doing something.
    if (...) { // want to break;
        ongoing.setFalse();
    }
});
Run Code Online (Sandbox Code Playgroud)