中断或从 Java 8 流 forEach 返回?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23308193/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 21:48:03  来源:igfitidea点击:

Break or return from Java 8 stream forEach?

javaforeachlambdajava-8

提问by Tapas Bose

When using external iterationover an Iterablewe use breakor returnfrom enhanced for-each loop as:

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

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

How can we breakor returnusing the internal iterationin a Java 8 lambda expression like:

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

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

采纳答案by Jesper

If you need this, you shouldn't use forEach, but one of the other methods available on streams; which one, depends on what your goal is.

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

For example, if the goal of this loop is to find the first element which matches some predicate:

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

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

(Note: This will not iterate the whole collection, because streams are lazily evaluated - it will stop at the first object that matches the condition).

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

If you just want to know if there's an element in the collection for which the condition is true, you could use anyMatch:

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

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

回答by Jon Skeet

Eitheryou need to use a method which uses a predicate indicating whether to keep going (so it has the break instead) oryou need to throw an exception - which is a very ugly approach, of course.

要么您需要使用一种方法,该方法使用指示是否继续前进的谓词(因此它有中断),要么您需要抛出异常 - 当然,这是一种非常丑陋的方法。

So you could write a forEachConditionalmethod like this:

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

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

Rather than Predicate<T>, you might want to define your own functional interface with the same general method (something taking a Tand returning a bool) but with names that indicate the expectation more clearly - Predicate<T>isn't ideal here.

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

回答by Julian Pieles

Below you find the solution I used in a project. Instead forEachjust use allMatch:

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

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

回答by Kanagavelu Sugumar

For maximal performance in parallel operations use findAny() which is similar to findFirst().

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

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

However If a stable result is desired, use findFirst() instead.

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

Also note that matching patterns (anyMatch()/allMatch) will return only boolean, you will not get matched object.

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

回答by frhack

You can use java8 + rxjava.

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

回答by Mohammad Adnan

I have achieved by something like this

我已经通过这样的事情实现了

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

回答by Honza Zidek

This ispossible for Iterable.forEach()(but not reliably with Stream.forEach()). The solution is not nice, but it ispossible.

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

WARNING: You should not use it for controlling business logic, but purely for handling an exceptional situation which occurs during the execution of the forEach(). Such as a resource suddenly stops being accessible, one of the processed objects is violating a contract (e.g. contract says that all the elements in the stream must not be nullbut suddenly and unexpectedly one of them is null) etc.

警告:您不应将其用于控制​​业务逻辑,而应纯粹用于处理在forEach(). 例如资源突然停止可访问,其中一个处理的对象违反了合同(例如,合同规定流中的所有元素不得null但突然且意外地其中之一是null)等。

According to the documentation for Iterable.forEach():

根据文档Iterable.forEach()

Performs the given action for each element of the Iterableuntilall elements have been processed or the action throws an exception... Exceptions thrown by the action are relayed to the caller.

为 的每个元素执行给定的操作,Iterable直到所有元素都已处理或操作引发异常...操作引发的异常将转发给调用者。

So you throw an exception which will immediately break the internal loop.

所以你抛出一个异常会立即中断内部循环。

The code will be something like this - I cannot say I like itbut it works. You create your own class BreakExceptionwhich extends RuntimeException.

代码将是这样的 -我不能说我喜欢它,但它有效。您创建自己的类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
}

Notice that the try...catchis notaround the lambda expression, but rather around the whole forEach()method. To make it more visible, see the following transcription of the code which shows it more clearly:

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

回答by Aneesh Vijendran

A return in a lambda equals a continue in a for-each, but there is no equivalent to a break. You can just do a return to continue:

lambda 中的 return 等于 for-each 中的 continue,但没有等价于 break。您只需返回即可继续:

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

回答by tuga

You can achieve that using a mix of peek(..) and anyMatch(..).

您可以混合使用 peek(..) 和 anyMatch(..) 来实现这一点。

Using your example:

使用您的示例:

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

Or just write a generic util method:

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

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

And then use it, like this:

然后使用它,像这样:

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

回答by user_3380739

Update with Java 9+ with takeWhile:

使用 Java 9+ 更新takeWhile

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