it-swarm.com.de

Pause oder Rückkehr von Java 8 Stream für jeden?

Bei Verwendung von externe Iteration über eine Iterable verwenden wir break oder return aus der erweiterten for-each-Schleife als:

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

Wie können wir break oder return unter Verwendung von interne Iteration in einem Java 8-Lambda-Ausdruck wie:

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

Wenn Sie dies benötigen, sollten Sie nicht forEach verwenden, sondern eine der anderen Methoden, die für Streams verfügbar sind. welches, hängt davon ab, was dein Ziel ist.

Wenn das Ziel dieser Schleife beispielsweise darin besteht, das erste Element zu finden, das mit einem Prädikat übereinstimmt:

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

(Hinweis: Dadurch wird nicht die gesamte Auflistung durchlaufen, da Streams nur schleppend ausgewertet werden. Sie werden beim ersten Objekt angehalten, das der Bedingung entspricht.).

Wenn Sie nur wissen möchten, ob ein Element in der Auflistung die Bedingung erfüllt, können Sie anyMatch verwenden:

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

Dies ist für Iterable.forEach() möglich (jedoch nicht zuverlässig mit Stream.forEach()). Die Lösung ist nicht Nizza, aber es ist möglich.

WARNING: Sie sollten es nicht zur Steuerung der Geschäftslogik verwenden, sondern nur zur Behandlung einer Ausnahmesituation, die während der Ausführung der forEach() auftritt. Wenn eine Ressource plötzlich nicht mehr verfügbar ist, verletzt eines der verarbeiteten Objekte einen Vertrag (z. B. besagt der Vertrag, dass alle Elemente im Stream nicht null sein dürfen, aber plötzlich und unerwartet ist eines von ihnen null ) usw.

Entsprechend der Dokumentation für Iterable.forEach() :

Führt die angegebene Aktion für jedes Element von Iterable aus, bis alle Elemente verarbeitet wurden, oder die Aktion Löst eine Ausnahme aus ... Von der Aktion ausgelöste Ausnahmen werden an den Aufrufer weitergeleitet.

Sie lösen also eine Ausnahme aus, die die interne Schleife sofort unterbricht.

Der Code wird ungefähr so ​​aussehen - Ich kann nicht sagen, dass er mir gefällt , aber er funktioniert. Sie erstellen Ihre eigene Klasse BreakException, die RuntimeException erweitert.

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
}

Beachten Sie, dass sich try...catch nicht um den Lambda-Ausdruck , sondern um die gesamte Methode forEach() dreht. Um es besser sichtbar zu machen, sehen Sie sich die folgende Transkription des Codes an, die es deutlicher zeigt:

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

Ein Return in einem Lambda entspricht einem Continue in einem For-Each, aber es gibt kein Äquivalent zu einer Pause. Sie können einfach zurückkehren, um fortzufahren:

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

Unten finden Sie die Lösung, die ich in einem Projekt verwendet habe. Verwenden Sie stattdessen forEach nur allMatch:

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

Entweder Sie müssen eine Methode verwenden, die ein Prädikat verwendet, das angibt, ob Sie weitermachen möchten (stattdessen hat sie eine Unterbrechung) Oder Sie müssen eine Ausnahme auslösen - was sehr wichtig ist hässliche Annäherung natürlich.

Sie könnten also eine forEachConditional -Methode wie diese schreiben:

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

Anstelle von Predicate<T> möchten Sie möglicherweise Ihre eigene funktionale Schnittstelle mit derselben allgemeinen Methode definieren (etwas, das T verwendet und bool zurückgibt), jedoch mit Namen, die die Erwartung deutlicher angeben - Predicate<T> ist hier nicht ideal.

11
Jon Skeet

Sie können Java8 + rxjava verwenden.

//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

Verwenden Sie für maximale Leistung bei parallelen Operationen findAny (), das findFirst () ähnelt.

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

Wenn Sie jedoch ein stabiles Ergebnis wünschen, verwenden Sie stattdessen findFirst ().

Beachten Sie auch, dass übereinstimmende Muster (anyMatch ()/allMatch) nur Boolesche Werte zurückgeben. Sie erhalten kein übereinstimmendes Objekt.

6

Aktualisieren Sie mit Java 9+ mit 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

Sie können dies mit einer Mischung aus peek (..) und anyMatch (..) erreichen.

Verwenden Sie Ihr Beispiel:

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

Oder schreiben Sie einfach eine generische util-Methode:

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

Und dann benutze es so:

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

Ich habe durch so etwas erreicht

  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

Was ist mit diesem:

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

Wobei BooleanWrapper eine Klasse ist, die Sie implementieren müssen, um den Fluss zu steuern.

1
Thomas Decaux