Chuyển đổi Iterable thành Stream bằng Java 8 JDK


418

Tôi có một giao diện trả về java.lang.Iterable<T>.

Tôi muốn thao tác kết quả đó bằng cách sử dụng API Java 8 Stream.

Tuy nhiên Iterable không thể "stream".

Bạn có biết cách sử dụng Iterable dưới dạng Stream mà không chuyển đổi nó thành List không?


2
Nếu bạn có thể lặp lại, tại sao không sử dụng một vòng lặp để kiểm tra tình trạng hoặc giá trị của nó hoặc những gì đã từng xảy ra?
Afzaal Ahmad Zeeshan

26
@AfzaalAhmadZeeshan vì các luồng tốt hơn nhiều
Sleiman Jneidi

1
Như tôi đã nói, tôi cần thực hiện một số thao tác trong danh sách đó (bộ lọc, ánh xạ). Tôi muốn sử dụng API JDK Java 8 mới -> Luồng. nhưng Iterable không phải là "SteamAble"
rayman

Có vẻ kỳ lạ mà myIterable.stream()không tồn tại!
Josh M.

7
@Guillaume: Có, nhưng Stream.of(iterable)sản xuất Stream<Iterable<Object>>.
Matthias Braun

Câu trả lời:


571

Có một câu trả lời tốt hơn nhiều so với sử dụng spliteratorUnknownSizetrực tiếp, cả hai đều dễ dàng hơn và có kết quả tốt hơn. Iterablecó một spliterator()phương thức, vì vậy bạn chỉ nên sử dụng phương thức đó để lấy bộ chia của bạn. Trong trường hợp xấu nhất, đó là cùng một mã (cách triển khai mặc định sử dụng spliteratorUnknownSize), nhưng trong trường hợp phổ biến hơn, trong đó bạn Iterableđã là một bộ sưu tập, bạn sẽ có được một trình phân tách tốt hơn và do đó hiệu suất truyền phát tốt hơn (thậm chí có thể song song tốt). Nó cũng ít mã hơn:

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

Như bạn có thể thấy, nhận được một luồng từ Iterable(cũng xem câu hỏi này ) không phải là rất đau đớn.


109
Một phương thức tĩnh trên Streamsẽ là tốt, ví dụ Stream.ofIterable(iterable).
cướp

59
@robinst Đây không phải là một thiếu sót; đó là một sự lựa chọn có chủ ý (và được tranh luận cao). Thách thức là điều này đã tồn tại, nó sẽ quá dễ dàng để tiếp cận nó mà không hiểu sự đánh đổi đi kèm với nó. Vì Iterable rất trừu tượng, nên một phương pháp như vậy sẽ dẫn đến luồng hoạt động kém nhất có thể (không hỗ trợ song song, không có thông tin kích thước hoặc đặc điểm (được sử dụng để tối ưu hóa các lựa chọn thực hiện)). Buộc nhiều kết quả suy nghĩ hơn trong các API tốt hơn trên toàn bộ hệ sinh thái. Đây là sự đánh đổi giữa "những gì tốt nhất cho mã XYZ" so với "những gì tốt nhất cho tất cả các mã Java."
Brian Goetz

2
Dựa trên lời giải thích của bạn, tôi tò mò tại sao chúng tôi có Collection.stream () mà không phải Iterable.stream (). Có vẻ như lý do bỏ qua Iterable.stream () (hoặc Stream.ofIterable ()) áp dụng như nhau cho Collection.stream ().
Qualidafial


11
@BrianGoetz Có vẻ như hầu hết mọi người không ở trong mức độ hiểu bạn đã nói ở trên hoặc không quan tâm. họ chỉ muốn viết mã đơn giản bằng cách gọi API đơn giản. Mặt khác, những điều đó (song song ...) có thể không quan trọng đối với hầu hết các hoạt động lặp lại hàng ngày.
user_3380739


23

Bạn có thể dễ dàng tạo Streamra một Iterablehoặc Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}

2
Bạn phải viết hàm này một lần và sau đó chỉ cần gọi nó. Tại sao một cuộc gọi để stream(...)làm lộn xộn mã của bạn?
gex tự sát

1
Muốn làm điều đó Nội tuyến ngắn và thanh lịch .. bạn phải Tôi có thể viết chức năng này một lần .. nhưng tôi đang bỏ mã (và không thêm mã). dù sao câu trả lời này là đúng bởi vì đó là cách để chuyển đổi này.
rayman

Chức năng nhập khẩu tĩnh nói. ngắn gọn và thanh lịch. (mặc dù không nhất thiết phải minh bạch)
aepurniet

9

Tôi muốn đề xuất sử dụng thư viện JOOL , nó ẩn ma thuật spliterator đằng sau cuộc gọi Seq.seq (có thể lặp lại) và cũng cung cấp một loạt các chức năng hữu ích bổ sung.


7

Vì vậy, như một câu trả lời khác được đề cập, Guava đã hỗ trợ cho việc này bằng cách sử dụng:

Streams.stream(iterable);

Tôi muốn nhấn mạnh rằng việc thực hiện làm một cái gì đó hơi khác so với các câu trả lời khác được đề xuất. Nếu Iterablelà loại Collectionhọ đúc nó.

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}

5

Tôi đã tạo lớp này:

public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

Tôi nghĩ rằng nó hoàn toàn có thể đọc được vì bạn không phải suy nghĩ về bộ chia và booleans (isParallel).


4

Một cách giải quyết rất đơn giản cho vấn đề này là tạo ra một Streamable<T>giao diện mở rộng Iterable<T>chứa một default <T> stream()phương thức.

interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

Bây giờ bất kỳ ai trong số bạn Iterable<T>có thể được truyền phát một cách tầm thường chỉ bằng cách khai báo chúng implements Streamable<T>thay vì Iterable<T>.


0

Nếu bạn tình cờ sử dụng Vavr (trước đây gọi là Javaslang), việc này có thể dễ dàng như:

Iterable i = //...
Stream.ofAll(i);

-1

Một cách khác để làm điều đó, với Java 8 và không có lib bên ngoài:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
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.