Không có sự khác biệt ngữ nghĩa giữa một chuỗi chuỗi các lời gọi và một loạt các lời gọi lưu trữ các giá trị trả về trung gian. Như vậy, đoạn mã sau là tương đương:
a = object.foo();
b = a.bar();
c = b.baz();
và
c = object.foo().bar().baz();
Trong cả hai trường hợp, mỗi phương pháp được gọi vào kết quả của sự thỉnh nguyện trước. Nhưng trong trường hợp sau, kết quả trung gian không được lưu trữ nhưng bị mất trong lần gọi tiếp theo. Trong trường hợp API luồng, kết quả trung gian không được sử dụng được sử dụng sau khi bạn đã gọi phương thức tiếp theo trên đó, do đó chuỗi là cách tự nhiên sử dụng luồng vì thực chất nó đảm bảo rằng bạn không gọi nhiều phương thức trên một tham chiếu trả về.
Tuy nhiên, bạn không nên lưu trữ tham chiếu đến luồng miễn là bạn tuân theo hợp đồng không sử dụng tham chiếu được trả về nhiều lần. Bằng cách sử dụng nó theo cách của họ như trong câu hỏi của bạn, tức là ghi đè biến với kết quả của lời gọi tiếp theo, bạn cũng đảm bảo rằng bạn không gọi nhiều phương thức trên tham chiếu trả lại, do đó, đó là cách sử dụng chính xác.Tất nhiên, điều này chỉ hoạt động với kết quả trung gian cùng loại, vì vậy khi bạn đang sử dụng map
hoặc flatMap
, nhận luồng của loại tham chiếu khác, bạn không thể ghi đè biến cục bộ. Sau đó, bạn phải cẩn thận để không sử dụng biến cục bộ cũ một lần nữa, nhưng, như đã nói, miễn là bạn không sử dụng nó sau lần gọi tiếp theo, không có gì sai với lưu trữ trung gian.
Đôi khi, bạn có để lưu trữ, ví dụ:
try(Stream<String> stream = Files.lines(Paths.get("myFile.txt"))) {
stream.filter(s -> !s.isEmpty()).forEach(System.out::println);
}
Lưu ý rằng mã tương đương với các phương thức sau:
try(Stream<String> stream = Files.lines(Paths.get("myFile.txt")).filter(s->!s.isEmpty())) {
stream.forEach(System.out::println);
}
và
try(Stream<String> srcStream = Files.lines(Paths.get("myFile.txt"))) {
Stream<String> tmp = srcStream.filter(s -> !s.isEmpty());
// must not be use variable srcStream here:
tmp.forEach(System.out::println);
}
Họ là tương đương vì forEach
luôn gọi vào kết quả của filter
mà luôn luôn viện dẫn trên kết quả của Files.lines
và không quan trọng kết quả là hoạt động close()
cuối cùng được gọi d như đóng cửa ảnh hưởng đến toàn bộ đường ống.
Để đặt trong một câu, cách bạn sử dụng, là chính xác.
Tôi thậm chí thích để làm điều đó theo cách đó, như không chaining một hoạt động limit
khi bạn không muốn áp dụng giới hạn là cách rõ ràng nhất để biểu hiện ý định của bạn. Nó cũng đáng chú ý là sự lựa chọn thay thế đề nghị có thể làm việc trong nhiều trường hợp, nhưng họ không ngữ nghĩa tương đương:
.limit(condition? aLimit: Long.MAX_VALUE)
giả định rằng số lượng tối đa các yếu tố, bạn đã bao giờ có thể gặp phải, là Long.MAX_VALUE
nhưng con suối có thể có nhiều yếu tố hơn thế, chúng thậm chí có thể là vô hạn.
.limit(condition? aLimit: list.size())
khi nguồn luồng là list
, đang phá vỡ đánh giá lười của luồng. Về nguyên tắc, một nguồn luồng có thể thay đổi một cách hợp pháp có thể được tùy ý thay đổi cho đến khi hành động đầu cuối được bắt đầu. Kết quả sẽ phản ánh tất cả các sửa đổi được thực hiện cho đến thời điểm này. Khi bạn thêm hoạt động trung gian kết hợp list.size()
, tức là kích thước thực tế của danh sách tại thời điểm này, các sửa đổi tiếp theo được áp dụng cho bộ sưu tập giữa điểm này và hoạt động của thiết bị đầu cuối có thể biến giá trị này thành một ý nghĩa khác với ý nghĩa "thực sự không có giới hạn" ngữ nghĩa.
Hãy so sánh với “Non Interference” section of the API documentation:
Đối với nguồn suối well-behaved, các nguồn có thể được sửa đổi trước khi phẫu thuật thiết bị đầu cuối bắt đầu và những thay đổi sẽ được phản ánh trong các yếu tố bảo hiểm. Ví dụ: hãy xem xét mã sau:
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
String s = sl.collect(joining(" "));
Danh sách đầu tiên được tạo bao gồm hai chuỗi: "một"; và hai". Sau đó, một luồng được tạo từ danh sách đó. Tiếp theo danh sách được sửa đổi bằng cách thêm một chuỗi thứ ba: "ba". Cuối cùng, các yếu tố của luồng được thu thập và kết hợp với nhau. Vì danh sách đã được sửa đổi trước khi hoạt động thu thập thiết bị đầu cuối bắt đầu, kết quả sẽ là một chuỗi "một hai ba".
Tất nhiên, đây là trường hợp góc hiếm gặp như bình thường, một lập trình viên sẽ xây dựng một đường dẫn toàn bộ luồng mà không sửa đổi bộ sưu tập nguồn ở giữa. Tuy nhiên, vẫn còn tồn tại ngữ nghĩa khác nhau và nó có thể biến thành một lỗi rất khó tìm thấy khi bạn nhập một trường hợp góc như vậy.
Hơn nữa, vì chúng không tương đương, API luồng sẽ không bao giờ nhận ra các giá trị này là "thực sự không có giới hạn". Ngay cả việc chỉ định Long.MAX_VALUE
ngụ ý rằng việc triển khai luồng phải theo dõi số lượng phần tử được xử lý để đảm bảo rằng giới hạn đã được tuân thủ. Do đó, không thêm hoạt động limit
có thể có lợi thế hiệu suất đáng kể so với việc thêm giới hạn với số mà người lập trình dự kiến sẽ không bao giờ bị vượt quá.
Có vẻ như bạn không thu được đầu ra của 'stream.filter()'. – whaleberg
Nếu bạn muốn viết nó trong một dòng, bạn có thể viết '.limit (giới hạn! = -1? Giới hạn: Long.MAX_VALUE)' nhưng tôi sẽ không viết. – zapl
@whaleberg, @WillShackleford, @Peter: Lỗi của tôi, tôi quên gán giá trị của luồng đã lọc cho 'luồng' trong mã ví dụ ban đầu của tôi. Tôi đoán tôi hiểu các luồng không chính xác. Tôi đọc rằng tất cả các luồng phải được theo sau bởi một hoạt động đầu cuối để chúng được thực hiện, vì vậy tôi nghĩ rằng nó không chính xác để lưu trữ luồng kết quả trong cùng một biến 'stream'. Tôi sẽ cập nhật câu hỏi ban đầu của mình. – Lani