Konversi Iterable ke Stream menggunakan Java 8 JDK

420

Saya memiliki antarmuka yang mengembalikan java.lang.Iterable<T>.

Saya ingin memanipulasi hasil itu menggunakan Java 8 Stream API.

Namun Iterable tidak dapat "streaming".

Adakah yang tahu cara menggunakan Iterable sebagai Stream tanpa mengubahnya menjadi Daftar?

rayman
sumber
2
Jika Anda bisa mengulanginya, mengapa tidak menggunakan perulangan untuk memeriksa kondisi atau nilainya atau apa?
Afzaal Ahmad Zeeshan
26
@AfzaalAhmadZeeshan karena aliran jauh lebih baik
Sleiman Jneidi
1
Seperti yang saya katakan, saya perlu melakukan beberapa maniuplations pada daftar itu (filter, pemetaan). Saya ingin menggunakan Java 8 JDK API -> Stream. tapi Iterable bukan "SteamAble"
rayman
Sepertinya aneh itu myIterable.stream()tidak ada!
Josh M.
7
@Guillaume: Ya, tapi Stream.of(iterable)hasilkan Stream<Iterable<Object>>.
Matthias Braun

Jawaban:

573

Ada jawaban yang jauh lebih baik daripada menggunakan spliteratorUnknownSizelangsung, yang keduanya lebih mudah dan mendapatkan hasil yang lebih baik. Iterablememiliki spliterator()metode, jadi Anda harus menggunakannya untuk mendapatkan spliterator Anda. Dalam kasus terburuk, ini adalah kode yang sama (menggunakan implementasi default spliteratorUnknownSize), tetapi dalam kasus yang lebih umum, di mana Anda Iterablesudah menjadi koleksi, Anda akan mendapatkan spliterator yang lebih baik, dan karena itu kinerja streaming yang lebih baik (mungkin bahkan paralelisme yang baik). Itu juga lebih sedikit kode:

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

Seperti yang Anda lihat, mendapatkan aliran dari Iterable(lihat juga pertanyaan ini ) tidak terlalu menyakitkan.

Brian Goetz
sumber
109
Metode statis pada Streamakan menyenangkan, misalnya Stream.ofIterable(iterable).
robinst
59
@robinst Ini bukan kelalaian; itu adalah pilihan yang disengaja (dan sangat diperdebatkan). Tantangannya adalah bahwa jika ini ada, akan terlalu mudah untuk mencapainya tanpa memahami pengorbanan yang menyertainya. Karena Iterable sangat abstrak, metode seperti itu akan menghasilkan aliran berkinerja terburuk yang mungkin (tidak ada dukungan paralel, tidak ada informasi ukuran atau karakteristik (digunakan untuk mengoptimalkan pilihan eksekusi)). Memaksa lebih banyak pemikiran menghasilkan API yang lebih baik di seluruh ekosistem. Ini adalah tradeoff dari "apa yang terbaik untuk kode XYZ" vs "apa yang terbaik untuk semua kode Java."
Brian Goetz
2
Berdasarkan penjelasan Anda, saya ingin tahu mengapa kami mendapat Collection.stream () tetapi tidak Iterable.stream (). Sepertinya alasan untuk menghilangkan Iterable.stream () (atau Stream.ofIterable ()) berlaku sama untuk Collection.stream ().
qualidafial
6
@ equalidafial Lihat stackoverflow.com/questions/23114015/… .
Brian Goetz
11
@BrianGoetz Sepertinya sebagian besar orang tidak berada di level untuk memahami yang Anda katakan di atas atau tidak peduli. mereka hanya ingin menulis kode sederhana dengan memanggil API sederhana. Di sisi lain, hal-hal itu (paralel ...) mungkin tidak penting untuk sebagian besar operasi iterable harian.
user_3380739
71

Jika Anda dapat menggunakan perpustakaan Guava, sejak versi 21, Anda dapat menggunakan

Streams.stream(iterable)
numéro6
sumber
2
Atau, jika Anda terjebak pada versi yang lebih lama, gunakan Lists.newArrayList(Iterable).
Jacob van Lingen
1
Implementasi Guava saat ini tidak lebih buruk daripada jawaban yang diterima: github.com/google/guava/blob/master/guava/src/com/google/common/…
Vadzim
23

Anda dapat dengan mudah membuat Streamdari Iterableatau Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
tidak ada
sumber
2
Anda harus menulis fungsi ini sekali dan kemudian panggil saja. Mengapa ada panggilan untuk stream(...)mengacaukan kode Anda?
gexicide
1
Ingin melakukannya Sebaris pendek dan elegan .. Anda benar saya bisa menulis fungsi ini sekali .. tapi saya ke menjatuhkan kode (dan tidak menambahkan kode). Pokoknya jawaban ini benar karena itulah cara untuk mengkonversi ini.
rayman
fungsi impor statis kata. pendek dan elegan. (meskipun tidak harus transparan)
aepurniet
9

Saya ingin menyarankan menggunakan perpustakaan JOOL , ia menyembunyikan sihir spliterator di belakang panggilan Seq.seq (iterable) dan juga menyediakan sejumlah fungsionalitas tambahan yang bermanfaat.

Shaggie
sumber
7

Jadi sebagai jawaban lain yang disebutkan Guava memiliki dukungan untuk ini dengan menggunakan:

Streams.stream(iterable);

Saya ingin menyoroti bahwa implementasi melakukan sesuatu yang sedikit berbeda dari jawaban yang disarankan. Jika Iterableadalah tipe Collectionmereka melemparkannya.

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
  );
}
Alex
sumber
5

Saya telah membuat kelas ini:

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

Saya pikir itu sangat mudah dibaca karena Anda tidak perlu berpikir tentang spliterator dan boolean (isParallel).

gt
sumber
4

Solusi yang sangat sederhana untuk masalah ini adalah membuat Streamable<T>antarmuka yang diperluas Iterable<T>yang menampung default <T> stream()metode.

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

Sekarang setiap dari Anda Iterable<T>s dapat trivial dibuat streamable hanya dengan menyatakan mereka implements Streamable<T>bukan Iterable<T>.

OldCurmudgeon
sumber
0

Jika Anda menggunakan Vavr (sebelumnya dikenal sebagai Javaslang), ini bisa semudah:

Iterable i = //...
Stream.ofAll(i);
Grzegorz Piwowarek
sumber
-1

Cara lain untuk melakukannya, dengan Java 8 dan tanpa lib eksternal:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Greg Witczak
sumber