Saya dapat menambahkan stream atau elemen tambahan, seperti ini:
Stream stream = Stream.concat(stream1, Stream.concat(stream2, Stream.of(element));
Dan saya dapat menambahkan hal-hal baru saat saya pergi, seperti ini:
Stream stream = Stream.concat(
Stream.concat(
stream1.filter(x -> x!=0), stream2)
.filter(x -> x!=1),
Stream.of(element))
.filter(x -> x!=2);
Tapi ini jelek, karena concat
statis. Jika concat
metode instan, contoh di atas akan lebih mudah dibaca:
Stream stream = stream1.concat(stream2).concat(element);
Dan
Stream stream = stream1
.filter(x -> x!=0)
.concat(stream2)
.filter(x -> x!=1)
.concat(element)
.filter(x -> x!=2);
Pertanyaanku adalah:
1) Apakah ada alasan bagus mengapa concat
statis? Atau adakah metode contoh yang setara yang saya lewatkan?
2) Bagaimana pun, apakah ada cara yang lebih baik untuk melakukan ini?
java
concat
java-8
java-stream
MarcG
sumber
sumber
Jawaban:
Jika Anda menambahkan impor statis untuk Stream.concat dan Stream.of , contoh pertama dapat ditulis sebagai berikut:
Mengimpor metode statis dengan nama generik dapat menghasilkan kode yang menjadi sulit dibaca dan dipelihara ( polusi namespace ). Jadi, mungkin lebih baik untuk membuat metode statis Anda sendiri dengan nama yang lebih bermakna. Namun, untuk demonstrasi saya akan tetap menggunakan nama ini.
Dengan dua metode statis ini (opsional dikombinasikan dengan impor statis), dua contoh dapat ditulis sebagai berikut:
Kode sekarang jauh lebih pendek. Namun, saya setuju bahwa keterbacaannya belum membaik. Jadi saya punya solusi lain.
Dalam banyak situasi, Kolektor dapat digunakan untuk memperluas fungsionalitas stream. Dengan dua Kolektor di bagian bawah, dua contoh dapat ditulis sebagai berikut:
Satu-satunya perbedaan antara sintaks yang Anda inginkan dan sintaks di atas adalah, bahwa Anda harus mengganti concat (...) dengan collect (concat (...)) . Dua metode statis dapat diimplementasikan sebagai berikut (opsional digunakan dalam kombinasi dengan impor statis):
Tentu saja ada kekurangan dengan solusi ini yang harus disebutkan. collect adalah operasi terakhir yang menghabiskan semua elemen aliran. Selain itu, concat kolektor membuat ArrayList perantara setiap kali digunakan dalam rantai. Kedua operasi dapat memiliki dampak signifikan pada perilaku program Anda. Namun, jika keterbacaan lebih penting daripada kinerja , itu mungkin masih menjadi pendekatan yang sangat membantu.
sumber
concat
kolektor lebih mudah dibaca. Tampaknya aneh untuk memiliki metode statis parameter tunggal bernama seperti ini, dan juga digunakancollect
untuk penggabungan.It's a bad idea to import static methods with names
? Saya benar-benar tertarik - Saya merasa itu membuat kode lebih ringkas dan mudah dibaca dan banyak orang yang saya tanya berpikiran sama. Mau memberikan beberapa contoh mengapa itu umumnya buruk?compare(reverse(getType(42)), of(6 * 9).hashCode())
? Perhatikan bahwa saya tidak mengatakan bahwa impor statis adalah ide yang buruk, tetapi impor statis untuk nama generik sepertiof
danconcat
sedang.Sayangnya jawaban ini mungkin sedikit atau tidak membantu sama sekali, tetapi saya melakukan analisis forensik dari Java Lambda Mailing list untuk melihat apakah saya dapat menemukan penyebab desain ini. Inilah yang saya temukan.
Pada awalnya ada metode instance untuk Stream.concat (Stream)
Di milis saya dapat dengan jelas melihat metode awalnya diimplementasikan sebagai metode contoh, seperti yang Anda baca di utas ini oleh Paul Sandoz, tentang operasi concat.
Di dalamnya mereka membahas isu-isu yang dapat timbul dari kasus-kasus di mana aliran dapat menjadi tak terbatas dan apa arti gabungan dalam kasus-kasus itu, tetapi saya tidak berpikir itu adalah alasan untuk modifikasi.
Anda lihat di utas lain ini bahwa beberapa pengguna awal JDK 8 mempertanyakan perilaku metode instance concat ketika digunakan dengan argumen nol.
Namun, utas lain ini mengungkapkan bahwa desain metode concat sedang dalam diskusi.
Refactored ke Streams.concat (Stream, Stream)
Tetapi tanpa penjelasan apa pun, tiba-tiba, metode diubah menjadi metode statis, seperti yang dapat Anda lihat di utas ini tentang menggabungkan aliran . Ini mungkin satu-satunya utas surat yang memberi sedikit cahaya tentang perubahan ini, tetapi tidak cukup jelas bagi saya untuk menentukan alasan refactoring. Tetapi kita dapat melihat mereka melakukan komit di mana mereka menyarankan untuk memindahkan
concat
metode dariStream
dan ke dalam kelas pembantuStreams
.Refactored ke Stream.concat (Stream, Stream)
Kemudian, itu dipindahkan lagi dari
Streams
keStream
, tetapi sekali lagi, tidak ada penjelasan untuk itu.Jadi, intinya, alasan desainnya tidak sepenuhnya jelas bagi saya dan saya tidak dapat menemukan penjelasan yang baik. Saya kira Anda masih bisa mengajukan pertanyaan di milis.
Beberapa Alternatif untuk Rangkaian Aliran
Ini thread lain oleh Michael Hixson membahas / bertanya tentang cara-cara lain untuk menggabungkan / concat sungai
sumber
public static <T> Stream<T> concat(Stream<T>... streams) { return Stream.of(streams).reduce(Stream.empty(), Stream::concat);}
@SafeVarargs private static <T> Stream<T> concat(Stream<? extends T>... streams) { return Stream.of(streams).reduce(Stream.empty(),Stream::concat).map(Function.identity());}
Function.identity()
peta? Bagaimanapun, ia mengembalikan argumen yang sama dengan yang diterimanya. Ini seharusnya tidak berpengaruh pada aliran yang dihasilkan. Apakah saya melewatkan sesuatu?return Stream.of(streams).reduce(Stream.empty(),Stream::concat)
mengembalikan Stream <? extends T>. (Suatu <T> adalah subtipe dari Sesuatu <? extends T>, bukan sebaliknya, sehingga tidak dapat dilemparkan).map(identity())
Cast tambahan <? meluas T> ke <T>. Hal ini terjadi berkat pencampuran argumen tipe metode 'java 8' dan metode pengembalian tipe dan tanda tangan map (). Sebenarnya itu Function. <T> identitas ().? extends T
, karena Anda dapat menggunakan konversi penangkapan . Bagaimanapun, ini potongan kode intisari saya. Mari kita lanjutkan diskusi di Intisari.Pustaka StreamEx saya memperluas fungsionalitas Stream API. Secara khusus ia menawarkan metode seperti menambahkan dan menambahkan yang memecahkan masalah ini (secara internal mereka gunakan
concat
). Metode-metode ini dapat menerima aliran lain atau koleksi atau array varargs. Menggunakan perpustakaan saya masalah Anda dapat diselesaikan dengan cara ini (perhatikan bahwax != 0
terlihat aneh untuk aliran non-primitif):Omong-omong, ada juga jalan pintas untuk
filter
operasi Anda :sumber
Kerjakan saja:
di mana
identity()
impor statisFunction.identity()
.Menggabungkan beberapa aliran ke dalam satu aliran sama dengan meratakan aliran.
Namun, sayangnya, untuk beberapa alasan tidak ada
flatten()
metode aktifStream
, jadi Anda harus menggunakanflatMap()
fungsi identitas.sumber
Anda dapat menggunakan metode Guava , yang akan sangat singkat dengan impor statis:
Streams
.
concat(Stream<? extends T>... streams)
sumber
Jika Anda tidak keberatan menggunakan Perpustakaan Pihak Ketiga cyclops-react memiliki tipe Stream tambahan yang akan memungkinkan Anda melakukan hal itu melalui operator append / prepend.
Nilai individual, array, iterables, Streaming, atau aliran reaktif. Penerbit dapat ditambahkan dan ditambahkan sebagai metode instan.
[Pengungkapan Saya adalah pengembang utama cyclops-react]
sumber
Pada akhirnya saya tidak tertarik untuk menggabungkan aliran, tetapi untuk mendapatkan hasil gabungan dari pemrosesan setiap elemen dari semua aliran tersebut.
Meskipun menggabungkan aliran mungkin rumit (sehingga utas ini), menggabungkan hasil pemrosesan mereka cukup mudah.
Kunci untuk menyelesaikannya adalah membuat kolektor Anda sendiri dan memastikan bahwa fungsi pemasok untuk kolektor baru mengembalikan koleksi yang sama setiap kali ( bukan yang baru ), kode di bawah ini menggambarkan pendekatan ini.
sumber
Bagaimana dengan menulis metode konser Anda sendiri?
Setidaknya ini membuat contoh pertama Anda jauh lebih mudah dibaca.
sumber