Di Jawa, apa keuntungan dari stream over loop? [Tutup]

133

Saya ditanyai dalam sebuah wawancara dan saya tidak yakin saya memberikan jawaban terbaik yang bisa saya miliki. Saya menyebutkan bahwa Anda dapat melakukan pencarian paralel dan bahwa nilai nol ditangani oleh beberapa cara yang saya tidak ingat. Sekarang saya sadar saya sedang memikirkan Opsional. Apa yang kulewatkan di sini? Mereka mengklaim itu kode yang lebih baik atau lebih ringkas tetapi saya tidak yakin saya setuju.


Mempertimbangkan betapa ringkasnya jawaban itu, sepertinya ini bukanlah pertanyaan yang terlalu luas.


Jika mereka mengajukan pertanyaan ini di wawancara, dan jelas mereka, tujuan apa yang bisa memecahnya berfungsi selain untuk membuatnya lebih sulit untuk menemukan jawaban? Maksud saya, apa yang Anda cari? Saya dapat memecah pertanyaan dan memiliki semua sub-pertanyaan dijawab tetapi kemudian membuat pertanyaan orang tua dengan tautan ke semua pertanyaan ... sepertinya cukup konyol. Sementara kita sedang mengerjakannya, tolong beri saya contoh pertanyaan yang kurang luas. Saya tahu tidak ada cara untuk menanyakan hanya sebagian dari pertanyaan ini dan masih mendapatkan jawaban yang bermakna. Saya bisa mengajukan pertanyaan yang sama persis dengan cara yang berbeda. Misalnya, saya bisa bertanya, "Apa tujuan stream melayani?" atau "Kapan saya akan menggunakan aliran alih-alih untuk loop?" atau "Mengapa repot-repot dengan stream bukannya untuk loop?" Ini semua adalah pertanyaan yang persis sama.

... atau itu dianggap terlalu luas karena seseorang memberikan jawaban multi-point yang sangat panjang? Terus terang siapa pun yang tahu bisa melakukan itu dengan hampir semua pertanyaan. Misalnya, jika Anda adalah salah satu penulis JVM, Anda mungkin bisa membicarakan tentang loop sepanjang hari ketika kebanyakan dari kita tidak bisa.

"Harap edit pertanyaan untuk membatasi masalah khusus dengan detail yang cukup untuk mengidentifikasi jawaban yang memadai. Hindari mengajukan beberapa pertanyaan sekaligus. Lihat halaman Bagaimana Mengajukan Pertanyaan untuk membantu menjelaskan pertanyaan ini."

Seperti disebutkan di bawah ini, jawaban yang memadai telah diberikan yang membuktikan bahwa ada satu dan cukup mudah untuk diberikan.

pengguna447607
sumber
7
Ini berdasarkan opini imho. Secara pribadi, saya lebih suka stream karena membuat kode lebih mudah dibaca. Hal ini memungkinkan untuk menulis apa yang Anda inginkan daripada caranya . Selain itu, itu benar-benar buruk untuk melakukan hal-hal menakjubkan dengan one-liners.
Arnaud Denoyelle
19
Bahkan jika itu adalah garis 30, satu liner? Saya tidak menyukai rantai panjang.
user447607
1
Selain itu, semua yang saya cari di sini adalah respons yang tepat untuk wawancara. Inilah satu-satunya "opini" yang penting.
user447607
1
Berbicara secara pendidikan, pertanyaan ini menyelamatkan saya dari degradasi pada wawancara di masa depan juga, @slim benar-benar memahaminya, tetapi secara industri, ini juga berbicara tentang bagaimana bahasa pemrograman Microsoft membangun karier mereka dalam merobek bahasa java, dan akhirnya java membalas dendam dengan merobek dari Ekspresi Lambda dan streaming dari lawan, mari kita lihat apa yang akan dilakukan java tentang Structs and Unions di masa depan :)
ShayHaned
5
Perhatikan bahwa stream hanya mengetuk sebagian kecil dari kekuatan dalam pemrograman fungsional: - /
Thorbjørn Ravn Andersen

Jawaban:

252

Menarik bahwa pertanyaan wawancara menanyakan tentang kelebihan, tanpa bertanya tentang kelemahan, karena keduanya ada.

Streaming adalah gaya yang lebih deklaratif . Atau gaya yang lebih ekspresif . Mungkin dianggap lebih baik untuk menyatakan maksud Anda dalam kode, daripada menjelaskan bagaimana hal itu dilakukan:

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

... mengatakan dengan sangat jelas bahwa Anda memfilter elemen yang cocok dari daftar, sedangkan:

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

Mengatakan "Saya sedang melakukan loop". Tujuan dari loop dikubur lebih dalam dalam logika.

Streaming sering terser . Contoh yang sama menunjukkan ini. Terser tidak selalu lebih baik, tetapi jika Anda bisa terse dan ekspresif pada saat yang sama, jauh lebih baik.

Streaming memiliki afinitas yang kuat dengan fungsi . Java 8 memperkenalkan lambdas dan antarmuka fungsional, yang membuka seluruh kotak mainan teknik yang kuat. Streaming menyediakan cara paling nyaman dan alami untuk menerapkan fungsi ke urutan objek.

Streaming mendorong lebih sedikit mutabilitas . Ini adalah semacam yang terkait dengan aspek pemrograman fungsional - jenis program yang Anda tulis menggunakan aliran cenderung jenis program di mana Anda tidak memodifikasi objek.

Streaming mendorong kopling yang lebih longgar . Kode penanganan aliran Anda tidak perlu tahu sumber arus, atau metode penghentian akhirnya.

Streaming dapat dengan singkat mengekspresikan perilaku yang cukup canggih . Sebagai contoh:

 stream.filter(myfilter).findFirst();

Sekilas mungkin terlihat seolah-olah itu menyaring seluruh aliran, lalu mengembalikan elemen pertama. Tetapi pada kenyataannya findFirst()mendorong seluruh operasi, sehingga efisien berhenti setelah menemukan satu item.

Streaming menyediakan ruang untuk keuntungan efisiensi masa depan . Beberapa orang telah membuat tolok ukur dan menemukan bahwa aliran berulir tunggal dari in-memory Lists atau array dapat lebih lambat daripada loop yang setara. Ini masuk akal karena ada lebih banyak objek dan overhead dalam permainan.

Tapi skala aliran. Selain dukungan bawaan Java untuk operasi aliran paralel, ada beberapa perpustakaan untuk pengurangan peta terdistribusi menggunakan Streams sebagai API, karena modelnya sesuai.

Kerugian?

Kinerja : forLoop through array sangat ringan baik dalam hal heap maupun penggunaan CPU. Jika kecepatan mentah dan penghematan memori adalah prioritas, menggunakan aliran lebih buruk.

Keakraban . Dunia ini penuh dengan programmer prosedural yang berpengalaman, dari banyak latar belakang bahasa, untuk siapa loop dikenal dan stream adalah novel. Di beberapa lingkungan, Anda ingin menulis kode yang biasa bagi orang semacam itu.

Overhead kognitif . Karena sifat deklaratifnya, dan peningkatan abstraksi dari apa yang terjadi di bawahnya, Anda mungkin perlu membangun model mental baru tentang bagaimana kode terkait dengan eksekusi. Sebenarnya Anda hanya perlu melakukan ini ketika ada kesalahan, atau jika Anda perlu menganalisis kinerja atau bug yang sangat halus. Ketika "hanya bekerja", itu hanya berfungsi.

Debugger membaik, tetapi bahkan sekarang, ketika Anda melangkah melalui kode aliran dalam debugger, itu bisa lebih sulit daripada loop setara, karena loop sederhana sangat dekat dengan variabel dan lokasi kode yang bekerja dengan debugger tradisional.

ramping
sumber
4
Saya pikir itu akan adil untuk rapi bahwa hal-hal seperti aliran menjadi lebih umum dan sekarang muncul dalam banyak bahasa yang umum digunakan yang tidak terlalu berorientasi FP.
Casey
5
Mengingat pro dan kontra yang tercantum di sini, saya pikir stream TIDAK layak untuk apa pun selain penggunaan yang sangat sederhana (sedikit logika jika / kemudian / lain, tidak banyak panggilan bersarang atau lambdas dll), di bagian kinerja non-kritis
Henrik Kjus Alstad
1
@HenrikKjusAlstad Itu sama sekali bukan takeaway yang saya maksudkan untuk berkomunikasi. Streaming matang, kuat, ekspresif, dan sepenuhnya sesuai untuk kode tingkat produksi.
langsing
Oh, maksudku aku tidak akan menggunakannya dalam produksi. Melainkan, saya akan default ke loop kuno / seandainya dll, daripada stream, terutama jika aliran yang dihasilkan akan terlihat kompleks. Saya yakin ada kegunaan di mana aliran akan mengalahkan loop dan jika dalam kejelasan, tetapi lebih sering daripada tidak, saya pikir itu "terikat", atau kadang-kadang bahkan sebaliknya. Jadi, saya memberi bobot pada argumen overhead kognitif untuk tetap berpegang pada cara lama.
Henrik Kjus Alstad
1
@lijepdam - tetapi Anda masih memiliki kode yang mengatakan "Saya mengulangi daftar ini (lihat di dalam lingkaran untuk mengetahui alasannya)", ketika "beralih daftar" bukan tujuan inti dari kode.
langsing
16

Disamping menyenangkan secara sintaksis, Streaming dirancang untuk bekerja dengan kumpulan data yang berpotensi besar tanpa batas, sedangkan array, Koleksi, dan hampir setiap kelas Java SE yang mengimplementasikan Iterable sepenuhnya ada dalam memori.

Kelemahan dari Stream adalah filter, pemetaan, dll., Tidak dapat membuang pengecualian yang diperiksa. Ini membuat Streaming pilihan yang buruk untuk, katakanlah, operasi I / O menengah.

VGR
sumber
7
Tentu saja, Anda juga dapat mengulangi sumber yang tidak terbatas.
langsing
Tetapi jika elemen-elemen untuk diproses bertahan dalam DB, bagaimana Anda menggunakan Streaming? Seorang junior dev bisa tergoda untuk membaca semuanya dalam Koleksi hanya untuk menggunakan Streaming. Dan itu akan menjadi bencana.
Lluis Martinez
2
@LluisMartinez pustaka klien DB yang baik akan mengembalikan sesuatu seperti Stream<Row>- atau dimungkinkan untuk menulis sendiri Streamimplementasi yang membungkus operasi kursor hasil DB.
ramping
Streaming yang diam-diam mengabaikan pengecualian adalah bug yang baru-baru ini saya gigit. Tidak intuitif.
xxfelixxx
@xxfelixxx Streaming tidak diam-diam mengabaikan pengecualian. Coba jalankan ini:Arrays.asList("test", null).stream().forEach(s -> System.out.println(s.length()));
VGR
8
  1. Anda salah sadari: operasi paralel menggunakan Streams, bukan Optionals.

  2. Anda bisa mendefinisikan metode yang bekerja dengan stream: menjadikannya sebagai parameter, mengembalikannya, dll. Anda tidak bisa menentukan metode yang menggunakan perulangan sebagai parameter. Ini memungkinkan operasi aliran yang rumit sekali dan menggunakannya berkali-kali. Perhatikan bahwa Java memiliki kelemahan di sini: metode Anda harus dipanggil someMethod(stream)sebagai berlawanan dengan aliran itu sendiri stream.someMethod(), jadi mencampurnya menyulitkan membaca: coba lihat urutan operasi di

    myMethod2(myMethod(stream.transform(...)).filter(...))

    Banyak bahasa lain (C #, Kotlin, Scala, dll) memungkinkan beberapa bentuk "metode ekstensi".

  3. Bahkan ketika Anda hanya membutuhkan operasi berurutan, dan tidak ingin menggunakannya kembali, sehingga Anda bisa menggunakan stream atau loop, operasi sederhana pada stream mungkin berhubungan dengan perubahan yang cukup rumit dalam loop.

Alexey Romanov
sumber
Jelaskan 1. Bukankah antarmuka opsional merupakan sarana penanganan null dalam rantai? Mengenai 3, itu masuk akal karena dengan filter hubung singkat, metode ini hanya akan dipanggil untuk kejadian tertentu. Efisien. Masuk akal bahwa saya dapat menyatakan bahwa menggunakannya mengurangi kebutuhan untuk menulis kode tambahan yang perlu diuji dll. Setelah ditinjau, saya tidak yakin apa yang Anda maksud dengan kasus berurutan di 2.
user447607
1. Optionaladalah alternatif untuk null, tetapi tidak ada hubungannya dengan operasi paralel. Kecuali "Sekarang saya sadar saya sedang memikirkan Opsional" dalam pertanyaan Anda hanya berbicara tentang nullpenanganan?
Alexey Romanov
Saya telah mengubah urutan 2 dan 3 dan sedikit memperluas keduanya.
Alexey Romanov
6

Anda mengulang urutan (array, koleksi, masukan, ...) karena Anda ingin menerapkan beberapa fungsi ke elemen urutan.

Streaming memberi Anda kemampuan untuk menyusun fungsi pada elemen urutan dan memungkinkan untuk mengimplementasikan fungsi yang paling umum (misalnya memetakan, memfilter, menemukan, menyortir, mengumpulkan, ...) terlepas dari case konkret.

Oleh karena itu diberikan beberapa tugas pengulangan dalam banyak kasus Anda dapat mengekspresikannya dengan kode lebih sedikit menggunakan Streaming, yaitu Anda mendapatkan keterbacaan .

wero
sumber
4
Yah, bukan hanya keterbacaan saja. Kode yang tidak harus Anda tulis adalah kode yang tidak harus Anda uji.
user447607
3
sepertinya Anda akan mendapatkan jawaban yang bagus untuk wawancara Anda juga
wero
6

Saya akan mengatakan paralelisasi yang sangat mudah digunakan. Coba iterasi jutaan entri secara paralel dengan for for loop. Kami pergi ke banyak CPU, tidak lebih cepat; jadi semakin mudah untuk berjalan secara paralel semakin baik, dan denganStream s ini sangat mudah.

Yang paling saya sukai adalah verbositas yang mereka tawarkan. Butuh sedikit waktu untuk memahami apa yang sebenarnya mereka lakukan dan hasilkan sebagai lawan dari bagaimana mereka melakukannya.

Eugene
sumber