从Java 8流forEach中断或返回?

当在Iterable上使用外部迭代时,我们使用增强的for-each循环中的breakreturn:

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

如何使用Java 8 lambda表达式中的内部迭代breakreturn,例如:

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

要么你需要使用一个方法,该方法使用一个谓词来指示是否继续(所以它有break代替)你需要抛出一个异常——当然,这是一个非常丑陋的方法。

所以你可以这样写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>在这里不是理想的。

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

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

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

(注意:这不会遍历整个集合,因为流是惰性计算的——它将在第一个匹配条件的对象处停止)。

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

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

下面是我在一个项目中使用的解决方案。相反,forEach只需使用allMatch:

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

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

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

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

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

你可以使用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));

我所取得的成就

  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;
}

这个对于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
}

注意,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
}

lambda中的return等于for-each中的continue,但不等同于break。你可以直接返回继续:

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

你可以混合使用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>
});

使用takeWhile更新到Java 9+:

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

这个呢:

final BooleanWrapper condition = new BooleanWrapper();
someObjects.forEach(obj -> {
if (condition.ok()) {
// YOUR CODE to control
condition.stop();
}
});

其中BooleanWrapper是一个必须实现来控制流的类。

int valueToMatch = 7;
Stream.of(1,2,3,4,5,6,7,8).anyMatch(val->{
boolean isMatch = val == valueToMatch;
if(isMatch) {
/*Do whatever you want...*/
System.out.println(val);
}
return isMatch;
});

它只会在找到匹配的地方执行操作,在找到匹配后停止迭代。

我建议使用anyMatch。例子:-

return someObjects.stream().anyMatch(obj ->
some_condition_met;
);

你可以参考这个帖子来理解anyMatch:- https://beginnersbook.com/2017/11/java-8-stream-anymatch-example/ < / p >

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");
}