Interruzione o ritorno dal flusso Java 8 forEach?


Quando si utilizza iterazione esterna su un Iterable usiamo break o return da enhanced per-ogni ciclo come:

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

Come possiamo break o return usando l'iterazione interna in un'espressione lambda Java 8 come:

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

11 answers

Se ne hai bisogno, non dovresti usare forEach, ma uno degli altri metodi disponibili sugli stream; quale, dipende da quale sia il tuo obiettivo.

Ad esempio, se l'obiettivo di questo ciclo è trovare il primo elemento che corrisponde a qualche predicato:

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

(Nota: questo non itererà l'intera raccolta, perché i flussi vengono valutati pigramente - si fermerà al primo oggetto che corrisponde alla condizione).

Se vuoi solo sapere se c'è un elemento nella raccolta per cui la condizione è vera, è possibile utilizzare anyMatch:

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

Questo è possibile per Iterable.forEach() (ma non in modo affidabile con Stream.forEach()). La soluzione non è bella, ma è possibile.

ATTENZIONE: Non si dovrebbe usare per controllare la logica di business, ma puramente per la gestione di una situazione eccezionale che si verifica durante l'esecuzione del forEach(). Ad esempio, una risorsa smette improvvisamente di essere accessibile, uno degli oggetti elaborati sta violando un contratto (ad es. null ma improvvisamente e inaspettatamente uno di loro è null) ecc.

Secondo la documentazione per Iterable.forEach():

Esegue l'azione data per ogni elemento del Iterable fino a quandotutti gli elementi sono stati elaborati o l'azione genera un'eccezione... Le eccezioni generate dall'azione vengono inoltrate al chiamante.

Quindi lanci un'eccezione che interromperà immediatamente il ciclo interno.

Il codice sarà qualcosa di simile - Non posso dire che mi piace ma funziona. Si crea la propria classe BreakException che estende 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
}

Si noti che try...catch è non attorno all'espressione lambda, ma piuttosto attorno all'intero metodo forEach(). Per renderlo più visibile, vedere la seguente trascrizione del codice che lo mostra più chiaramente:

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 ritorno in un lambda equivale a una continua in un for-each, ma non c'è equivalente a una pausa. Puoi semplicemente fare un ritorno per continuare:

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

Di seguito trovi la soluzione che ho usato in un progetto. Invece forEach basta usare allMatch:

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

O è necessario utilizzare un metodo che utilizza un predicato che indica se continuare (quindi ha invece la pausa) o è necessario lanciare un'eccezione - che è un approccio molto brutto, ovviamente.

Quindi potresti scrivere un metodo forEachConditional come questo:

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

Piuttosto che Predicate<T>, potresti voler definire la tua interfaccia funzionale con lo stesso metodo generale (qualcosa che prende un T e restituisce un bool) ma con nomi che indicano l'aspettativa più chiaramente - Predicate<T> non è l'ideale qui.

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

Puoi usare 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

Per prestazioni massime in operazioni parallele utilizzare findAny () che è simile a findFirst().

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

Tuttavia, se si desidera un risultato stabile, utilizzare invece findFirst ().

Si noti inoltre che i modelli corrispondenti (anyMatch()/allMatch) restituiranno solo booleano, non si otterrà un oggetto abbinato.

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

Ho raggiunto qualcosa di simile

  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

È possibile ottenere che utilizzando un mix di peek(..) e anyMatch(..).

Usando il tuo esempio:

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

O semplicemente scrivere un metodo util generico:

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

E poi usarlo, in questo modo:

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

Non è facile farlo con le API fornite in JDK. Ma possiamo farlo con AbacusUtil . Ecco il codice di esempio:

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

Divulgazione: Sono lo sviluppatore di AbacusUtil.

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

Che dire di questo:

Condizione BooleanWrapper finale = nuovo BooleanWrapper();

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

Dove BooleanWrapper è una classe che devi implementare per controllare il flusso.

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