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?
})
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);
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
}
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;
}
})
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;
});
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.
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));
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.
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();
}
});
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>
});
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;
}
Qu'en est-il de celui-ci:
final BooleanWrapper condition = new BooleanWrapper();
someObjects.forEach(obj -> {
if (condition.ok()) {
// YOUR CODE to control
condition.stop();
}
});
Où BooleanWrapper
est une classe que vous devez implémenter pour contrôler le flux.