2015-11-16 49 views
6

Tôi tự hỏi liệu tôi có thể thêm hoạt động vào luồng không, dựa trên một số loại điều kiện được đặt bên ngoài luồng. Ví dụ: tôi muốn thêm hoạt động giới hạn vào luồng nếu biến số limit của tôi không bằng -1.Điều kiện thêm hoạt động vào luồng Java 8

Mã của tôi hiện nay trông như thế này, nhưng tôi chưa thấy ví dụ khác về suối đang được sử dụng theo cách này, nơi một đối tượng Suối được bố trí đến kết quả của một hoạt động trung gian áp dụng trên chính nó:

// Do some stream stuff 
stream = stream.filter(e -> e.getTimestamp() < max); 

// Limit the stream 
if (limit != -1) { 
    stream = stream.limit(limit); 
} 

// Collect stream to list 
stream.collect(Collectors.toList()); 

Như đã nêu trong stackoverflow post này, bộ lọc không thực sự được áp dụng cho đến khi một hoạt động đầu cuối được gọi. Vì tôi gán lại giá trị của luồng trước khi một hoạt động đầu cuối được gọi, mã trên vẫn là một cách thích hợp để sử dụng các luồng Java 8?

+2

Có vẻ như bạn không thu được đầu ra của 'stream.filter()'. – whaleberg

+1

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

+0

@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

Trả lời

10

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(); 

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 để 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); 
} 

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á.

5

Tôi nghĩ rằng dòng đầu tiên của bạn cần phải:

stream = stream.filter(e -> e.getTimestamp() < max); 

để bạn sử dụng dòng trả về bởi bộ lọc trong các hoạt động tiếp theo chứ không phải là dòng gốc.

5

Có hai cách bạn có thể làm điều này

// Do some stream stuff 
List<E> results = list.stream() 
        .filter(e -> e.getTimestamp() < max); 
        .limit(limit > 0 ? limit : list.size()) 
        .collect(Collectors.toList()); 

HOẶC

// Do some stream stuff 
stream = stream.filter(e -> e.getTimestamp() < max); 

// Limit the stream 
if (limit != -1) { 
    stream = stream.limit(limit); 
} 

// Collect stream to list 
List<E> results = stream.collect(Collectors.toList()); 

Vì đây là chức năng lập trình bạn nên luôn luôn làm việc trên các kết quả của từng chức năng. Bạn nên đặc biệt tránh thay đổi bất cứ điều gì trong phong cách lập trình này và đối xử với mọi thứ như thể nó là bất biến nếu có thể.

Vì tôi gán lại giá trị của luồng trước khi hoạt động đầu cuối được gọi, mã trên vẫn là cách thích hợp để sử dụng luồng Java 8?

Nó sẽ hoạt động, tuy nhiên nó đọc là kết hợp mã hóa bắt buộc và chức năng. Tôi đề nghị viết nó như một dòng cố định theo câu trả lời đầu tiên của tôi.

+0

hoặc '.limit (giới hạn> 0? Giới hạn: Dài.MAX_VALUE)' trong trường hợp luồng không dựa trên bộ sưu tập. – zeroflagL

+0

@ Holger Điều gì đã xảy ra là một bản sao và dán mã từ câu hỏi và quên xóa nhận xét đó. Cảm ơn bạn đã chọn điều đó. –

Các vấn đề liên quan