Làm cách nào tôi có thể biến Stream thành Iterable? [bản sao]


8

Streamkế thừa một phương thức iterator () để tạo ra một Iterator.

Nhưng tôi cần một Iterablechứ không phải là một Iterator.

Ví dụ, đưa ra chuỗi này:

String input = "this\n" +
        "that\n" +
        "the_other";

Tôi cần phải truyền những phần của chuỗi như một Iterablethư viện cụ thể. Gọi input.lines()năng suất a Stream. Vì vậy, tôi sẽ được thiết lập nếu tôi có thể biến nó Streamthành một Iterabletrong những yếu tố của nó.


@Naman Liên quan, nhưng điều đó dường như ngược lại với những gì Basil đang làm trong phần hỏi đáp này.
Slaw

1
Một cách khác để có được một Iterable sẽ chỉ là thu thập vào Danh sách.
matt

Câu trả lời:


12

Như đã giải thích trong Tại sao Stream <T> không triển khai lặp lại <T>? , một người Iterablemang kỳ vọng có thể cung cấp Iteratornhiều hơn một lần, điều mà Streamkhông thể thực hiện được. Vì vậy, trong khi bạn có thể tạo Iterablera một Streammục đích sử dụng đặc biệt, bạn phải cẩn thận về việc liệu các nỗ lực lặp lại nhiều lần có thể tồn tại hay không.

Vì bạn đã nói, tôi cần chuyển các phần của chuỗi thành một Iterablethư viện cụ thể , không có giải pháp chung nào vì mã sử dụng Iterablenằm ngoài tầm kiểm soát của bạn.

Nhưng nếu bạn là người tạo luồng, có thể tạo hợp lệ Iterable, điều này sẽ lặp lại việc xây dựng luồng mỗi khi Iteratorđược yêu cầu:

Iterable<String> lines = () -> "this\nthat\nthe_other".lines().iterator();

Điều này đáp ứng kỳ vọng hỗ trợ một số lần lặp tùy ý, trong khi không tiêu tốn nhiều tài nguyên hơn một luồng khi chỉ đi qua một lần.

for(var s: lines) System.out.println(s);
lines.forEach(System.out::println);
System.out.println(String.join("\n", lines));

Giải thích tuyệt vời. Có thể đáng để thêm vào (như một phụ lục nâng cao hơn một chút) rằng trong khi có thể nói Iterable<String> lines = "this\nthat\nthe_other".lines()::iterator;đây là một trong những trường hợp hiếm hoi mà tham chiếu phương thức không giống như lambda và thực tế sẽ có tác dụng không mong muốn.
Klitos Kyriacou

3
Tham chiếu phương thức @KlitosKyriacou của biểu mẫu expression::namekhông bao giờ giống như biểu thức lambda, thậm chí không có System.out::println. Nhưng ở đây, chúng ta có một trong những trường hợp quan trọng
Holger

Trong trường hợp này, các dòng sẽ được gọi mỗi lần, nhưng tôi không thấy sự khác biệt giữa phương thức và lambda nếu Stream<String> lines = str.lines();sau đó bạn tạo Iterable<String> iter = lines::iteratorso sánh ()->lines.iterator().
matt

2
@matt không ai nói rằng có sự khác biệt giữa stream::iterator() -> stream.iterator(). Trong thực tế, câu trả lời của tôi đã nói chính xác, không có giải pháp chung nào để chuyển đổi một luồng hiện có thành một lần lặp hợp lệ cho phép nhiều lần lặp. Lặp đi lặp lại việc xây dựng luồng mỗi khi một trình lặp được yêu cầu, tức là ở đây nó có nghĩa là gọi lines()mỗi lần, là điều làm nên sự khác biệt.
Holger

3
@matt tốt, chính thức, nó không giống nhau. expression::namecó nghĩa là đánh giá expressionmột lần và nắm bắt kết quả , trong khi đó () -> expression.name(…)có nghĩa là đánh giá expressionmỗi lần cơ thể chức năng được đánh giá . Sự khác biệt là rất nhỏ khi “ expression” chỉ là một biến địa phương, nhưng thậm chí sau đó, nó khác nhau, tức là stream::iteratorsẽ cư xử khác so với () -> stream.iterator()khi streamnull. Vì vậy, tuyên bố của tôi vẫn giữ, expression::nameluôn khác với biểu thức lambda, câu hỏi là, nó quan trọng đến mức nào đối với trường hợp sử dụng cụ thể của tôi.
Holger

3

tl; dr

Chỉ cần diễn viên , không cần chuyển đổi.

Đúc Stream < String >để Iterable < String >.

Chi tiết

THẬN TRỌNG Xem Trả lời của Holger giải thích về sự nguy hiểm của việc sử dụng luồng được hỗ trợ Iterable.

Vâng, bạn có thể làm cho Iterabletừ một Stream.

Giải pháp rất đơn giản, nhưng không rõ ràng. Xem bài đăng này trên Câu hỏi thường gặp về Lambda của Maurice Naftalin .

Các iterator()phương pháp trên BaseStream(lớp cha của Stream) trả lại một Iteratorxảy ra trùng với cùng tên của iterator()phương pháp trả lại một Iteratortheo yêu cầu của Iterablegiao diện. Các chữ ký phương thức phù hợp. Vì vậy, chúng tôi thực sự có thể Streamchuyển sang một Iterable, không cần chuyển đổi.

Làm đầu vào của bạn.

String input = "this\n" +
        "that\n" +
        "the_other";
Stream < String > stream = input.lines() ;

Đúc mà Stream<String>để Iterable<String>.

Iterable< String > iterable = ( Iterable < String > ) stream ;  // Cast `Stream < String >` to `Iterable < String >`. 

Kiểm tra kết quả.

for ( String s : iterable ) 
{
    System.out.println( "s = " + s );
}

Xem này chạy trực tiếp tại IdeOne.com .

s = cái này

s = đó

s =

CAVEAT Cẩn thận với rủi ro của luồng được hỗ trợ Iterable. Giải thích trong câu trả lời đúng của Holger .


7
Về mặt kỹ thuật, bạn không chọn từ Stream<String>stream::iteratorkhông phải là loại Stream<String>.
kaya3

3
Tôi không chắc chắn đúc là thuật ngữ đúng. Bạn đang tạo một thể hiện Iterablethông qua một tham chiếu phương thức; các (Iterable<String>)chỉ nói với trình biên dịch mà giao diện chức năng là mục tiêu.
Slaw

@Slaw & @ kaya3 Những sự tinh tế này đã thoát khỏi sự hiểu biết của tôi. Tôi không thấy thế nào (Iterable<String>)là một diễn viên, nhưng tôi chắc chắn không hiểu tình huống này, vì những bình luận của bạn đã thúc giục tôi đặt thêm một loạt các dấu hiệu xung quanh ( (Iterable<String>) stream )khiến cho mã bị lỗi - chứng tỏ sự hiểu biết của tôi là thiếu sót. Vì vậy, vui lòng chỉnh sửa Câu trả lời của tôi để làm rõ hoặc đăng Câu trả lời của riêng bạn với lời giải thích tốt hơn.
Basil Bourque

4
@Slaw vấn đề là, đúng là có một diễn viên, nhưng không đúng khi nói rằng dàn diễn viên là giải pháp. Các giải pháp bao gồm một tham chiếu phương pháp và một diễn viên. Việc truyền có thể được thay thế bằng một cấu trúc khác cung cấp loại mục tiêu dự định, ví dụ: chuyển tham chiếu phương thức sang một phương thức mong đợi một lần lặp hoặc gán nó cho một biến có kiểu lặp. Nhưng giải pháp vẫn yêu cầu tham chiếu phương thức hoặc biểu thức lambda cho chuyển đổi. Vì vậy, thật vô nghĩa khi nói rằng chỉ dùng cast, thay vì chuyển đổi khi vẫn còn một chuyển đổi (mã bộ điều hợp) diễn viên.
Holger

2
Iterable thực sự là một giao diện chức năng, bạn đang tạo một Iterable với phương thức iterator được ghi đè bởi stream.iterator. Nó có hiệu quả sẽ (Iterable<String>)()->stream.iterator()hoặc thậm chí rõ ràng hơn new Iterable<String>(){ public Iterator<String> iterator(){ return stream.iterator();}. Vì vậy, bạn không truyền Stream thành Iterable, điều này sẽ thất bại.
matt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.