Wie kann ich überprüfen, ob a Stream
leer ist, und eine Ausnahme auslösen, wenn dies nicht der Fall ist, als nicht-terminale Operation?
Grundsätzlich suche ich nach etwas, das dem folgenden Code entspricht, ohne jedoch den dazwischen liegenden Stream zu materialisieren. Insbesondere sollte die Überprüfung nicht erfolgen, bevor der Stream tatsächlich von einer Terminaloperation verbraucht wird.
public Stream<Thing> getFilteredThings() {
Stream<Thing> stream = getThings().stream()
.filter(Thing::isFoo)
.filter(Thing::isBar);
return nonEmptyStream(stream, () -> {
throw new RuntimeException("No foo bar things available")
});
}
private static <T> Stream<T> nonEmptyStream(Stream<T> stream, Supplier<T> defaultValue) {
List<T> list = stream.collect(Collectors.toList());
if (list.isEmpty()) list.add(defaultValue.get());
return list.stream();
}
java
java-8
java-stream
Kopffüßer
quelle
quelle
Antworten:
Wenn Sie mit eingeschränkten parallelen Funktionen leben können, funktioniert die folgende Lösung:
Hier ist ein Beispielcode, der ihn verwendet:
Das Problem bei der (effizienten) parallelen Ausführung besteht darin, dass für die Unterstützung der Aufteilung des
Spliterator
Threads eine threadsichere Methode erforderlich ist, um festzustellen, ob eines der Fragmente einen threadsicheren Wert aufweist. Dann muss das letzte der ausgeführten FragmentetryAdvance
erkennen, dass es das letzte ist (und es konnte auch nicht vorrücken), das die entsprechende Ausnahme auslöst. Daher habe ich hier keine Unterstützung für die Aufteilung hinzugefügt.quelle
Die anderen Antworten und Kommentare sind insofern richtig, als man zum Untersuchen des Inhalts eines Streams eine Terminaloperation hinzufügen muss, wodurch der Stream "verbraucht" wird. Sie können dies jedoch tun und das Ergebnis wieder in einen Stream umwandeln, ohne den gesamten Inhalt des Streams zu puffern. Hier einige Beispiele:
Verwandeln Sie den Stream in einen
Iterator
, um ihn aufzurufenhasNext()
, und wenn dies zutrifft, verwandeln Sie denIterator
Rücken in einenStream
. Dies ist insofern ineffizient, als alle nachfolgenden Operationen auf dem Stream die IteratorenhasNext()
undnext()
Methoden durchlaufen , was auch impliziert, dass der Stream effektiv sequentiell verarbeitet wird (selbst wenn er später parallel gedreht wird). Auf diese Weise können Sie den Stream jedoch testen, ohne alle Elemente zu puffern.Es gibt wahrscheinlich eine Möglichkeit, dies mit a
Spliterator
anstelle von a zu tunIterator
. Dies ermöglicht möglicherweise, dass der zurückgegebene Stream dieselben Eigenschaften wie der Eingabestream aufweist, einschließlich der parallelen Ausführung.quelle
estimatedSize
undcharacteristics
könnte sogar Single-Thread - Performance verbessern. Es ist einfach passiert, dass ich dieSpliterator
Lösung geschrieben habe, während Sie dieIterator
Lösung veröffentlicht haben…tryAdvance
vor demStream
Do anrufen , wird die Faulheit desStream
zu einem „teilweise faulen“ Stream. Dies bedeutet auch, dass die Suche nach dem ersten Element keine parallele Operation mehr ist, da Sie zuerst teilen undtryAdvance
die geteilten Teile gleichzeitig ausführen müssen, um eine echte parallele Operation durchzuführen, soweit ich verstanden habe. Wenn der einzige TerminalbetriebfindAny
oder ähnlich ist, würde dies die gesamteparallel()
Anforderung zerstören .tryAdvance
vor dem Stream aufrufen und müssen jeden geteilten Teil in einen Proxy packen und die "hasAny" -Informationen aller gleichzeitigen Vorgänge selbst sammeln und sicherstellen, dass der letzte gleichzeitige Vorgang die gewünschte Ausnahme auslöst, wenn der Strom war leer. Viele Sachen…Sie müssen eine Terminaloperation für den Stream ausführen, damit einer der Filter angewendet wird. Daher können Sie nicht wissen, ob es leer sein wird, bis Sie es verbrauchen.
Das Beste, was Sie tun können, ist, den Stream mit einer
findAny()
Terminaloperation zu beenden , die beendet wird, wenn ein Element gefunden wird. Wenn jedoch keines vorhanden ist, muss die gesamte Eingabeliste durchlaufen werden, um dies herauszufinden.Dies würde Ihnen nur helfen, wenn die Eingabeliste viele Elemente enthält und einer der ersten die Filter durchläuft, da nur eine kleine Teilmenge der Liste verbraucht werden müsste, bevor Sie wissen, dass der Stream nicht leer ist.
Natürlich müssen Sie noch einen neuen Stream erstellen, um die Ausgabeliste zu erstellen.
quelle
anyMatch(alwaysTrue())
, ich denke , das ist in der Nähe vonhasAny
.anyMatch(alwaysTrue())
passt perfekt zu der beabsichtigten Semantik von dirhasAny
und gibt dir einboolean
stattOptional<T>
--- aber wir spalten hier die Haare :)alwaysTrue
ist ein Guaven-Prädikat.anyMatch(e -> true)
dann.Dies kann in vielen Fällen ausreichend sein
quelle
Ich denke, sollte ausreichen, um einen Booleschen Wert abzubilden
Im Code ist dies:
quelle
Stream.anyMatch()
Nach Stuarts Idee könnte dies folgendermaßen geschehen
Spliterator
:Ich denke, dies funktioniert mit parallelen Streams, da der
stream.spliterator()
Vorgang den Stream beendet und ihn dann nach Bedarf neu erstelltIn meinem Anwendungsfall benötigte ich einen Standardwert
Stream
anstelle eines Standardwerts. Das ist ziemlich einfach zu ändern, wenn dies nicht das ist, was Sie brauchenquelle
Spliterator
Ich frage mich, wie die beiden sich vergleichen.