Pause ou retour de Java 8 stream forEach?


Lors de l'utilisation de externe itération sur une Iterable nous utilisons break ou return renforcée pour chaque boucle:

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

Comment pouvons-nous break ou return utiliser l'itération interne dans une expression Java 8 lambda comme:

someObjects.forEach(obj -> {
   //what to do here?
})
Author: Lii, 2014-04-26

11 answers

Si vous en avez besoin, vous ne devriez pas utiliser forEach, mais l'une des autres méthodes disponibles sur streams; laquelle dépend de votre objectif.

Par exemple, si le but de cette boucle est de trouver le premier élément qui correspond à un prédicat:

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

(Remarque: Cela n'itérera pas toute la collection, car les flux sont évalués paresseusement - il s'arrêtera au premier objet qui correspond à la condition).

Si vous voulez juste savoir s'il y a un élément dans la collection pour laquelle la condition est vraie, vous pouvez utiliser anyMatch:

boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);
 254
Author: Jesper, 2016-03-17 13:49:49

Ce est possible pour Iterable.forEach() (mais pas de manière fiable avec Stream.forEach()). La solution n'est pas agréable, mais c' est possible.

WARNING : Vous ne devez pas l'utiliser pour contrôler la logique métier, mais uniquement pour gérer une situation exceptionnelle qui se produit lors de l'exécution du forEach(). Comme une ressource cesse soudainement d'être accessible, l'un des objets traités viole un contrat (par exemple, le contrat indique que tous les éléments du flux ne doivent pas être null, mais soudainement et de façon inattendue, l'un d'eux est null) etc.

, Selon la documentation pour Iterable.forEach():

Effectue l'action donnée, pour chaque élément de la Iterable jusqu', tous les éléments ont été traitées, ou l'action déclenche une exception... Les exceptions lancées par l'action sont relayées à l'appelant.

Vous lancez donc une exception qui cassera immédiatement la boucle interne.

Le code sera quelque chose comme ça - Je ne peux pas dire que je l'aime mais ça marche. Vous créez votre propre classe BreakException qui étend 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
}

Notez que la try...catch est pas autour de l'expression lambda, mais plutôt autour de l'ensemble forEach() méthode. Pour le rendre plus visible, voir la transcription suivante du code qui le montre plus clairement:

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
}
 40
Author: Honza Zidek, 2018-08-06 08:18:47

Un retour dans un lambda est égal à un continue dans un for-each, mais il n'y a pas d'équivalent à une pause. Vous pouvez simplement faire un retour pour continuer:

someObjects.forEach(obj -> {
   if (some_condition_met) {
      return;
   }
})
 20
Author: avijendr, 2016-05-25 22:22:33

Vous trouverez ci-dessous la solution que j'ai utilisée dans un projet. Au lieu de cela forEach utilisez simplement allMatch:

someObjects.allMatch(obj -> {
    return !some_condition_met;
});
 19
Author: Julian Liebl, 2015-12-23 16:22:58

Soit vous devez utiliser une méthode qui utilise un prédicat indiquant s'il faut continuer (donc il a la pause à la place) ou vous devez lever une exception - ce qui est une approche très laide, bien sûr.

Vous pouvez donc écrire une méthode forEachConditional comme ceci:

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

Plutôt que Predicate<T> , vous voudrez peut-être définir votre propre interface fonctionnelle avec la même méthode générale (quelque chose prenant un T et renvoyant un bool) mais avec des noms qui indiquent l'attente plus clairement - Predicate<T> n'est pas idéal ici.

 11
Author: Jon Skeet, 2014-04-26 07:49:19

Vous pouvez utiliser 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));
 8
Author: frhack, 2015-06-02 18:00:03

Pour des performances maximales dans les opérations parallèles, utilisez findAny () qui est similaire à FindFirst().

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

Cependant, si un résultat stable est souhaité, utilisez FindFirst() à la place.

Notez également que les modèles correspondants (anyMatch()/allMatch) ne renverront que booléen, vous n'obtiendrez pas d'objet correspondant.

 4
Author: Kanagavelu Sugumar, 2014-12-02 13:18:32

J'ai réalisé quelque chose comme ça

  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;
    }
 3
Author: Mohammad Adnan, 2015-06-24 09:50:21

Vous pouvez y parvenir en utilisant un mélange de peek(..) et anyMatch(..).

En utilisant votre exemple:

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

Ou écrivez simplement une méthode util générique:

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

Puis utilisez-le, comme ceci:

streamWhile(someObjects.stream(), obj -> <some_condition_met>, obj -> {
   <your code here>
});
 2
Author: tuga, 2016-11-08 15:11:19

Ce n'est pas facile de le faire par les API fournies dans JDK. Mais nous pouvons le faire par AbacusUtil. Voici un exemple de code:

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

Divulgation: Je suis le développeur de AbacusUtil.

 2
Author: user_3380739, 2017-06-09 18:32:15

Qu'en est-il de celui-ci:

Si vous avez besoin d'un BooleanWrapper, vous pouvez utiliser le BooleanWrapper.]}
someObjects.forEach(obj -> {
   if (condition.ok()) {
     // YOUR CODE to control
     condition.stop();
   }
});

BooleanWrapper est une classe que vous devez implémenter pour contrôler le flux.

 1
Author: Thomas Decaux, 2018-01-04 12:15:26