web-dev-qa-db-fra.com

Interrompre ou revenir du flux Java 8 pour chaque

Lors de l'utilisation de itération externe sur une Iterable, nous utilisons break ou return à partir de la boucle améliorée pour-chaque:

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

Comment pouvons-nous break ou return en utilisant le itération interne dans une expression Java 8 lambda telle que:

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

Si vous en avez besoin, n'utilisez pas forEach, mais utilisez l'une des autres méthodes disponibles dans les flux; lequel 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ère pas la totalité de la collection, car les flux sont évalués paresseusement - ils s'arrêteront au premier objet correspondant à la condition).

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

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

Ceci est possible pour Iterable.forEach() (mais pas de manière fiable avec Stream.forEach()). La solution n'est pas belle, 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 de la forEach(). Par exemple, si une ressource cesse soudainement d’être accessible, l’un des objets traités enfreint un contrat (par exemple, le contrat stipule que tous les éléments du flux ne doivent pas être null, mais soudainement et inopinément, l’un d’eux est null ) etc.

Selon la documentation de Iterable.forEach() :

Effectue l'action indiquée pour chaque élément de la Iterable jusqu'à ce que tous les éléments aient été traités ou l'action lève une exception ... Les exceptions levées par l'action sont relayées à l'appelant.

Donc, vous lancez une exception qui cassera immédiatement la boucle interne.

Le code sera quelque chose comme ceci - Je ne peux pas dire que j'aime ça mais cela fonctionne. 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 le try...catch est non pas autour de l'expression lambda, mais plutôt autour de la méthode forEach(). 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
}
48
Honza Zidek

Un retour dans un lambda équivaut à une continuation dans un pour chacun, 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;
   }
})
32
Aneesh Vijendran

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;
});
21
Julian Pieles

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

Ainsi, vous pourriez é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 pouvez définir votre propre interface fonctionnelle avec la même méthode générale (quelque chose qui prend un T et qui retourne un bool), mais avec des noms qui indiquent plus clairement l'attente - Predicate<T> n'est pas idéal ici.

11
Jon Skeet

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
frhack

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();

Toutefois, si vous souhaitez obtenir un résultat stable, utilisez plutôt findFirst ().

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

6
Kanagavelu Sugumar

Mise à jour avec Java 9+ avec takeWhile:

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

Vous pouvez y parvenir en mélangeant 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 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());
}

Et ensuite, utilisez-le comme ceci:

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

J'ai réussi 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
Mohammad Adnan

Qu'en est-il de celui-ci:

final BooleanWrapper condition = new 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
Thomas Decaux