当在Iterable上使用外部迭代时,我们使用break或return from enhanced for-each循环,如下:

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

如何在Java 8 lambda表达式中使用内部迭代中断或返回:

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

当前回答

您要么需要使用一个使用谓词指示是否继续的方法(因此它有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>在这里不是理想的。

其他回答

这对于Iterable.forEach()是可能的(但对于Stream.forEach()不可靠)。这个解决方案并不好,但它是可能的。

警告:您不应该使用它来控制业务逻辑,而应该纯粹用于处理forEach()执行期间发生的异常情况。例如,某个资源突然停止可访问,某个被处理的对象违反了契约(例如,契约规定流中的所有元素都不能为空,但突然且意外地有一个元素为空)等等。

根据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
}

您要么需要使用一个使用谓词指示是否继续的方法(因此它有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>在这里不是理想的。

更新到Java 9+与takeWhile:

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

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

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

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

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

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

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

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

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