¿Salto o regreso de Java 8 stream forEach?

Cuando utilizamos iteración externa sobre un Iterable usamos break o return de loop for-each mejorado como:

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

¿Cómo podemos break o return utilizando la iteración interna en una expresión lambda Java 8 como:

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

Si necesita esto, no debe usar forEach , sino uno de los otros métodos disponibles en las transmisiones; cuál, depende de cuál sea tu objective.

Por ejemplo, si el objective de este ciclo es encontrar el primer elemento que coincida con algún predicado:

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

(Nota: Esto no repetirá toda la colección, porque las secuencias se evalúan de forma diferida; se detendrá en el primer objeto que coincida con la condición).

Si solo quiere saber si hay un elemento en la colección para el cual la condición es verdadera, podría usar anyMatch :

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

Esto es posible para Iterable.forEach() (pero no de manera confiable con Stream.forEach() ). La solución no es buena, pero es posible.

ADVERTENCIA : No debe usarlo para controlar la lógica comercial, sino únicamente para manejar una situación excepcional que ocurre durante la ejecución de forEach() . Como un recurso deja de ser accesible repentinamente, uno de los objetos procesados ​​viola un contrato (por ejemplo, el contrato dice que todos los elementos en la transmisión no deben ser null pero de repente e inesperadamente uno de ellos es null ), etc.

De acuerdo con la documentación de Iterable.forEach() :

Realiza la acción dada para cada elemento de Iterable hasta que todos los elementos hayan sido procesados ​​o la acción arroje una excepción … Las excepciones lanzadas por la acción se transmiten a la persona que llama.

Entonces lanzas una excepción que romperá inmediatamente el ciclo interno.

El código será algo como esto: no puedo decir que me gusta, pero funciona. Usted crea su propia clase BreakException que extiende 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 } 

Tenga en cuenta que el try...catch no se encuentra alrededor de la expresión lambda, sino en todo el método forEach() . Para hacerlo más visible, vea la siguiente transcripción del código que lo muestra más claramente:

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

Un retorno en un lambda equivale a continuar en un for-each, pero no hay equivalente a un descanso. Puede hacer una devolución para continuar:

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

A continuación encontrará la solución que utilicé en un proyecto. En cambio, forEach vez que use allMatch :

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

O necesita usar un método que usa un predicado que indica si continuar (por lo que tiene el salto en su lugar) o necesita lanzar una excepción, que es un enfoque muy feo, por supuesto.

Entonces podrías escribir un método forEachConditional como este:

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

En lugar de Predicate , es posible que desee definir su propia interfaz funcional con el mismo método general (algo que toma una T y devuelve un bool ) pero con nombres que indican la expectativa más claramente: Predicate no es ideal aquí .

Puedes usar 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)); 

Para un rendimiento máximo en operaciones paralelas, use findAny () que es similar a findFirst ().

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

Sin embargo, si se desea un resultado estable, use findFirst () en su lugar.

También tenga en cuenta que los patrones de coincidencia (anyMatch () / allMatch) devolverán solo booleano, no obtendrá el objeto coincidente.

Lo logré por algo como esto

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

Puedes lograr eso usando una mezcla de peek (..) y anyMatch (..).

Usando tu ejemplo:

 someObjects.stream().peek(obj -> {  }).anyMatch(obj -> !); 

O simplemente escribe un método de utilidad genérico:

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

Y luego úsalo, así:

 streamWhile(someObjects.stream(), obj -> , obj -> {  }); 

No es fácil hacerlo con las API proporcionadas en JDK. Pero podemos hacerlo por AbacusUtil . Aquí hay un código de muestra:

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

Divulgación: soy el desarrollador de AbacusUtil.

¿Qué hay de este?

condición BooleanWrapper final = new BooleanWrapper ();

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

Donde BooleanWrapper es una clase que debe implementar para controlar el flujo.