TL; DR, điều này đã được giải quyết trong JDK-8075939 và được sửa trong Java 10 (và được hỗ trợ lại cho Java 8 trong JDK-8225328 ).
Khi xem xét triển khai ( ReferencePipeline.java
), chúng tôi thấy phương thức [ link ]
@Override
final void forEachWithCancel(Spliterator<P_OUT> spliterator, Sink<P_OUT> sink) {
do { } while (!sink.cancellationRequested() && spliterator.tryAdvance(sink));
}
sẽ được gọi cho findFirst
hoạt động. Điều đặc biệt cần quan tâm là sink.cancellationRequested()
cho phép kết thúc vòng lặp ở trận đấu đầu tiên. So sánh với [ liên kết ]
@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);
}
}
};
}
};
}
Phương thức tiến một mục sẽ kết thúc cuộc gọi forEach
trên luồng phụ mà không có bất kỳ khả năng kết thúc sớm hơn và chú thích ở đầu flatMap
phương thức thậm chí còn cho biết về tính năng vắng mặt này.
Vì đây không chỉ là một thứ tối ưu hóa vì nó ngụ ý rằng mã chỉ đơn giản bị hỏng khi luồng phụ là vô hạn, tôi hy vọng rằng các nhà phát triển sớm chứng minh rằng họ “có thể làm tốt hơn điều này”…
Để minh họa các hàm ý, trong khi Stream.iterate(0, i->i+1).findFirst()
hoạt động như mong đợi, Stream.of("").flatMap(x->Stream.iterate(0, i->i+1)).findFirst()
sẽ kết thúc trong một vòng lặp vô hạn.
Về đặc điểm kỹ thuật, hầu hết nó có thể được tìm thấy trong
chương “Hoạt động dòng và đường ống” của đặc điểm kỹ thuật gói :
…
Các hoạt động trung gian trả về một luồng mới. Họ luôn lười biếng ;
…
… Sự lười biếng cũng cho phép tránh kiểm tra tất cả dữ liệu khi không cần thiết; đối với các hoạt động như "tìm chuỗi đầu tiên dài hơn 1000 ký tự", chỉ cần kiểm tra vừa đủ các chuỗi để tìm một chuỗi có các đặc điểm mong muốn mà không cần kiểm tra tất cả các chuỗi có sẵn từ nguồn. (Hành vi này thậm chí còn trở nên quan trọng hơn khi luồng đầu vào là vô hạn và không chỉ lớn.)
…
Hơn nữa, một số hoạt động được coi là hoạt động đoản mạch . Một hoạt động trung gian là ngắn mạch nếu, khi được trình bày với đầu vào vô hạn, kết quả là nó có thể tạo ra một dòng hữu hạn. Hoạt động của thiết bị đầu cuối bị chập mạch nếu, khi xuất hiện với đầu vào vô hạn, nó có thể kết thúc trong thời gian hữu hạn. Việc xảy ra hiện tượng đoản mạch trong đường ống là điều kiện cần nhưng chưa đủ để quá trình xử lý dòng vô hạn kết thúc bình thường trong thời gian hữu hạn.
Rõ ràng là hoạt động đoản mạch không đảm bảo chấm dứt thời gian hữu hạn, ví dụ: khi bộ lọc không khớp với bất kỳ mục nào, quá trình xử lý không thể hoàn thành, nhưng việc triển khai không hỗ trợ bất kỳ chấm dứt nào trong thời gian hữu hạn bằng cách bỏ qua bản chất đoản mạch của một hoạt động khác xa với thông số kỹ thuật.