Tôi phải thừa nhận rằng tôi cũng gặp khó khăn khi lần đầu tiên tìm hiểu ý nghĩa thực tế của các đặc điểm và cảm giác rằng ý nghĩa của chúng không được giải quyết rõ ràng trong giai đoạn thực hiện Java 8 và được sử dụng không nhất quán cho điều đó lý do.
Cân nhắc Spliterator.IMMUTABLE
:
giá trị đặc trưng có nghĩa rằng nguồn yếu tố không thể được sửa đổi về mặt cấu trúc; có nghĩa là, các phần tử không thể được thêm vào, thay thế hoặc loại bỏ, do đó những thay đổi như vậy không thể xảy ra trong quá trình truyền tải.
Thật kỳ lạ để xem “thay thế” trong danh sách này, mà thường không được coi là một sự sửa đổi cấu trúc khi nói về một List
hoặc một mảng và do đó, suối và spliterator nhà máy chấp nhận một mảng báo cáo (có nghĩa là không nhân bản) IMMUTABLE
, như LongStream.of(…)
hoặc Arrays.spliterator(long[])
.
Nếu chúng ta giải thích điều này hào phóng hơn như “miễn là không thể quan sát được của khách hàng”, không có sự khác biệt đáng kể cho CONCURRENT
, như trong cả hai trường hợp một số yếu tố sẽ được báo cáo cho khách hàng mà không cách nào để nhận ra liệu chúng được thêm vào trong quá trình truyền tải hoặc cho dù một số chưa được báo cáo do xóa, vì không có cách nào để tua lại một trình tách và so sánh.
Các đặc điểm kỹ thuật tiếp tục:
Một Spliterator mà không báo cáo IMMUTABLE
hoặc CONCURRENT
dự kiến sẽ có một chính sách tài liệu (ví dụ ném ConcurrentModificationException
) liên quan đến sự can thiệp về cấu trúc phát hiện trong quá traversal.
Và đó là điều duy nhất có liên quan, một spliterator báo cáo một trong hai, hoặc IMMUTABLE
CONCURRENT
, đảm bảo sẽ không bao giờ ném một ConcurrentModificationException
.Tất nhiên, CONCURRENT
ngăn cản SIZED
ngữ nghĩa, nhưng điều đó không có hậu quả đối với mã máy khách.
Thực tế, những đặc điểm này không được sử dụng cho bất kỳ thứ gì trong API luồng, do đó, việc sử dụng chúng không nhất quán sẽ không bao giờ được chú ý ở đâu đó.
Đây cũng là lời giải thích tại sao mỗi hoạt động trung gian có tác dụng xóa CONCURRENT
, IMMUTABLE
và NONNULL
đặc điểm: việc thực hiện Dòng không sử dụng chúng và các lớp bên trong của nó đại diện cho nhà nước suối không duy trì chúng.
Tương tự như vậy, NONNULL
không được sử dụng bất cứ nơi nào, vì vậy nó không có cho con suối nào đó không có tác dụng. Tôi có thể theo dõi các vấn đề LongStream.of(…)
xuống để sử dụng nội bộ của Arrays.spliterator(long[], int, int)
mà các đại biểu để
Spliterators.spliterator(long[] array, int fromIndex, int toIndex, int additionalCharacteristics)
:
Các spliterator trở luôn luôn báo cáo đặc SIZED
và SUBSIZED
. Người gọi có thể cung cấp các đặc tính bổ sung cho trình phân tách để báo cáo. (Ví dụ: nếu biết rằng mảng sẽ không được sửa đổi thêm, hãy chỉ định IMMUTABLE
; nếu dữ liệu mảng được coi là có lệnh bắt gặp, hãy chỉ định ORDERED
). Phương pháp Arrays.spliterator(long[], int, int)
thường có thể được sử dụng thay thế, trả về một trình tách biệt báo cáo SIZED
, SUBSIZED
, IMMUTABLE
và ORDERED
.
Lưu ý (lần nữa) việc sử dụng không nhất quán của đặc tính IMMUTABLE
. Nó được xử lý lại giống như phải bảo đảm sự vắng mặt của bất kỳ sửa đổi nào, trong khi cùng lúc, Arrays.spliterator
và lần lượt Arrays.stream
và LongStream.of(…)
sẽ báo cáo đặc tính IMMUTABLE
, thậm chí theo đặc điểm kỹ thuật, mà không thể bảo đảm rằng người gọi sẽ không sửa đổi mảng. Trừ khi chúng ta xem xét việc thiết lập một phần tử không phải là một sửa đổi cấu trúc, nhưng sau đó, toàn bộ sự phân biệt trở nên vô nghĩa một lần nữa, vì các mảng không thể được sửa đổi về mặt cấu trúc.
Và nó đã chỉ định rõ ràng không có đặc tính NONNULL
. Mặc dù rõ ràng là các giá trị nguyên thủy không thể là null
và các lớp Spliterator.Abstract<Primitive>Spliterator
lúc nào cũng chèn một đặc tính NONNULL
, trình tách biệt được trả về bởi Spliterators.spliterator(long[],int,int,int)
không được kế thừa từ Spliterator.AbstractLongSpliterator
.
Điều xấu là, điều này không thể khắc phục được mà không thay đổi đặc điểm kỹ thuật, điều tốt là nó không có hậu quả gì.
Vì vậy, nếu chúng ta bỏ qua bất kỳ vấn đề với CONCURRENT
, IMMUTABLE
, hoặc NONNULL
, mà không có hậu quả, chúng tôi có
SIZED
và skip
& limit
. Đây là một vấn đề nổi tiếng, kết quả của cách thức skip
và limit
đã được API luồng thực hiện. Các triển khai khác có thể tưởng tượng được. Điều này cũng áp dụng cho sự kết hợp của một dòng vô hạn với một limit
, mà nên có một kích thước có thể dự đoán, nhưng cho việc thực hiện hiện tại, đã không.
Kết hợp Stream.concat(…)
với Stream.empty()
. Nghe có vẻ hợp lý khi một luồng trống không không áp đặt các ràng buộc vào thứ tự kết quả. Nhưng hành vi phát hành lệnh của Stream.concat(…)
khi chỉ có một đầu vào không có thứ tự, là vấn đề. Lưu ý rằng quá tích cực về thứ tự không có gì mới, hãy xem this Q&A về hành vi được xem là có chủ ý trước, nhưng sau đó đã được sửa vào cuối Java 8, cập nhật 60. Có lẽ, Stream.concat
cũng đã được thảo luận ngay tại thời điểm này …
Hành vi của .boxed()
rất dễ giải thích. Khi nó đã được thực hiện ngây thơ như .mapToObj(Long::valueOf)
, nó sẽ chỉ đơn giản là mất tất cả các kiến thức, như mapToObj
không thể giả định rằng kết quả vẫn được sắp xếp hoặc khác biệt. Nhưng điều này đã được sửa với Java 9. Ở đó, LongStream.range(0,10).boxed()
có các đặc tính SUBSIZED|SIZED|ORDERED|SORTED|DISTINCT
, duy trì tất cả các đặc điểm có liên quan đến việc triển khai.
Ai đó đã bỏ phiếu để đóng câu hỏi là "quá rộng" và tôi đã bị cám dỗ làm điều tương tự vì có vẻ như sáu câu hỏi trong một câu hỏi. Mặt khác, có thể có một lời giải thích tổng thể cho tất cả các chi tiết này ... – Nicolai
@Nicolai Tôi đã bỏ phiếu ban đầu ... nhưng quá tốt để đóng cửa, vì vậy tôi đã quay lại nó. Tôi thích điều này "tất cả-trong-một" cho một câu hỏi hay. Bên cạnh một vài người dùng Oracle (Stuart? Hoặc Brian?), Tôi chỉ mong Holger biết chính xác nội tại của những thứ này. Vui vẻ chờ đợi. :) – Eugene
* Tôi hiểu rằng '.mapToObj()' có thể mất NONNULL, IMMUTABLE và DISTI [N] CT *. Tại sao không SORTED? – shmosel