TL; DR, dies wurde in JDK-8075939 behoben und in Java 10 behoben (und in JDK-8225328 auf Java 8 zurückportiert ).
Wenn ReferencePipeline.java
wir uns die Implementierung ( ) ansehen, sehen wir die Methode [ Link ]
@Override
final void forEachWithCancel(Spliterator<P_OUT> spliterator, Sink<P_OUT> sink) {
do { } while (!sink.cancellationRequested() && spliterator.tryAdvance(sink));
}
welches für den findFirst
Betrieb aufgerufen wird. Das Besondere ist, dass Sie sink.cancellationRequested()
die Schleife beim ersten Spiel beenden können. Vergleiche mit [ Link ]
@Override
public final <R> Stream<R> flatMap(Function<? super P_OUT, ? extends Stream<? extends R>> mapper) {
Objects.requireNonNull(mapper);
return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
return new Sink.ChainedReference<P_OUT, R>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
try (Stream<? extends R> result = mapper.apply(u)) {
if (result != null)
result.sequential().forEach(downstream);
}
}
};
}
};
}
Die Methode zum Vorrücken eines Elements ruft forEach
den Sub-Stream auf, ohne dass eine frühere Beendigung möglich ist, und der Kommentar am Anfang der flatMap
Methode gibt sogar Auskunft über diese fehlende Funktion.
Da dies mehr als nur eine Optimierungssache ist, da dies impliziert, dass der Code einfach bricht, wenn der Substream unendlich ist, hoffe ich, dass die Entwickler bald beweisen, dass sie „besser als das können“…
Um die Auswirkungen zu veranschaulichen Stream.iterate(0, i->i+1).findFirst()
, Stream.of("").flatMap(x->Stream.iterate(0, i->i+1)).findFirst()
wird es , obwohl es wie erwartet funktioniert, in einer Endlosschleife enden.
In Bezug auf die Spezifikation finden Sie das meiste davon in der
Kapitel „Stream-Operationen und Pipelines“ der Paketspezifikation :
…
Zwischenoperationen geben einen neuen Stream zurück. Sie sind immer faul ;
…
… Durch Faulheit kann auch vermieden werden, dass alle Daten überprüft werden, wenn dies nicht erforderlich ist. Bei Operationen wie "Finden Sie die erste Zeichenfolge mit mehr als 1000 Zeichen" müssen Sie nur so viele Zeichenfolgen untersuchen, dass eine Zeichenfolge mit den gewünschten Eigenschaften gefunden wird, ohne alle von der Quelle verfügbaren Zeichenfolgen zu untersuchen. (Dieses Verhalten wird noch wichtiger, wenn der Eingabestream unendlich und nicht nur groß ist.)
…
Ferner werden einige Operationen als Kurzschlussoperationen angesehen . Eine Zwischenoperation ist kurzgeschlossen, wenn sie bei unendlicher Eingabe einen endlichen Strom erzeugen kann. Eine Terminaloperation ist kurzgeschlossen, wenn sie bei unendlicher Eingabe in endlicher Zeit beendet werden kann. Ein Kurzschlussbetrieb in der Pipeline ist eine notwendige, aber nicht ausreichende Bedingung, damit die Verarbeitung eines unendlichen Stroms normal in endlicher Zeit endet.
Es ist klar, dass eine Kurzschlussoperation keine endliche Zeitbeendigung garantiert, z. B. wenn ein Filter keinem Element entspricht, das die Verarbeitung nicht abschließen kann, sondern eine Implementierung, die keine Beendigung in endlicher Zeit durch einfaches Ignorieren unterstützt Der Kurzschlusscharakter einer Operation liegt weit außerhalb der Spezifikation.