Apa perbedaan antara metode map () dan flatMap () di Java 8?

706

Di Java 8, apa perbedaan antara Stream.map()dan Stream.flatMap()metode?

cassiomolin
sumber
55
Jenis tanda tangan agak menceritakan keseluruhan cerita. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Chris Martin
98
fwiw, tanda tangan jenis itu bahkan tidak terlihat seperti Java. (Saya tahu, saya tahu - tetapi untuk mengatakan itu menceritakan "seluruh cerita" wrt map / flatMap mengasumsikan banyak pengetahuan tentang "Java ++" yang baru & lebih baik)
michael
16
@ Michael Jenis tanda tangan itu terlihat seperti Haskell, bukan Java. Tapi itu tidak jelas apakah tanda tangan Java sebenarnya lebih mudah dibaca: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Stuart Marks
8
Ha, ya, saya mengacu pada "Java aktual". Seperti C ++, Java modern hampir tidak dapat dikenali oleh siapa pun yang mulai menggunakannya pada tahun 90-an (seperti yang saya lakukan, kedua bahasa). Hanya menjawab komentar, tanda tangan metode itu hampir tidak menceritakan "keseluruhan cerita", setidaknya tidak lagi, bukan tanpa eksposisi tambahan (atau dalam kasus komentator, terjemahan).
Michael
2
Yang mengatakan, seorang map's mapper lambda kembali R, sebuah flatMap' s mapper lambda pengembalian a Streamdari R( Stream<R>). Aliran yang dikembalikan oleh flatMapmapper 'adalah digabungkan secara efektif. Jika tidak, keduanya mapdan flatMapkembali Stream<R>; perbedaannya adalah apa lambdas mapper kembali, Rvs Stream<R>.
derekm

Jawaban:

816

Keduanya mapdan flatMapdapat diterapkan ke Stream<T>dan mereka berdua mengembalikan a Stream<R>. Perbedaannya adalah bahwa mapoperasi menghasilkan satu nilai output untuk setiap nilai input, sedangkan flatMapoperasi menghasilkan nilai angka acak (nol atau lebih) untuk setiap nilai input.

Ini tercermin dalam argumen untuk setiap operasi.

The mapoperasi mengambil Function, yang disebut untuk setiap nilai dalam aliran input dan menghasilkan satu nilai hasil, yang dikirim ke output stream.

The flatMapoperasi mengambil fungsi yang konseptual ingin mengkonsumsi satu nilai dan menghasilkan jumlah sewenang-wenang nilai-nilai. Namun, di Jawa, sulit untuk sebuah metode untuk mengembalikan jumlah nilai yang berubah-ubah, karena metode hanya dapat mengembalikan nol atau satu nilai. Orang bisa membayangkan API di mana fungsi mapper untuk flatMapmengambil nilai dan mengembalikan array atau aListnilai, yang kemudian dikirim ke output. Karena ini adalah pustaka stream, cara yang paling tepat untuk mewakili jumlah nilai pengembalian yang berubah-ubah adalah fungsi mapper sendiri untuk mengembalikan aliran! Nilai-nilai dari aliran dikembalikan oleh mapper dikeringkan dari aliran dan diteruskan ke aliran output. "Rumpun" nilai yang dikembalikan oleh setiap panggilan ke fungsi mapper tidak dibedakan sama sekali dalam aliran output, sehingga output dikatakan telah "diratakan."

Penggunaan umum adalah untuk fungsi mapper flatMapuntuk mengembalikan Stream.empty()jika ia ingin mengirim nilai nol, atau sesuatu seperti Stream.of(a, b, c)jika ia ingin mengembalikan beberapa nilai. Tapi tentu saja aliran apa pun dapat dikembalikan.

Stuart Marks
sumber
26
Bagi saya terdengar seperti flatMapoperasi adalah kebalikan dari flat. Sekali lagi, serahkan kepada Ilmuwan Komputer untuk mengubah istilah di kepala itu. Seperti fungsi yang "transparan" yang berarti Anda tidak dapat melihat apa pun yang dilakukannya, hanya hasilnya, sementara bahasa sehari-hari mengatakan Anda ingin proses menjadi transparan berarti Anda ingin setiap bagian dari itu dilihat.
coladict
45
@colict Cobalah untuk melihatnya dari perspektif yang berbeda: ini bukan kasus transparan di mana Anda dapat melihat inner bekerja melalui, tetapi seluruh fungsi itu sendiri transparan, yaitu tidak terlihat, untuk Anda - sambil tetap melakukan pekerjaan mereka dan membiarkan Anda melihat apa yang Anda ' sedang bekerja dengan. Dalam kasus ini, "flat" mengacu pada kebalikan dari "nested", flatmap menghapus satu level sarang dengan meratakan.
Zefiro
7
@coladict Hal "transparan" telah memakan kepalaku selama bertahun-tahun. Senang mengetahui setidaknya satu orang lainnya merasakan hal yang sama.
Ashok Bijoy Debnath
9
Meratakan berasal dari mengubah struktur 2 tingkat menjadi struktur tingkat tunggal, lihat jawaban Dici untuk contoh stackoverflow.com/a/26684582/6012102
andrzej.szmukala
26
Ini adalah penjelasan terbaik dari flatMap . Inilah yang membuat semuanya diklik: Nilai-nilai dari aliran yang dikembalikan oleh mapper dikeringkan dari aliran dan diteruskan ke aliran keluaran. "Rumpun" nilai yang dikembalikan oleh setiap panggilan ke fungsi mapper tidak dibedakan sama sekali dalam aliran output, sehingga output dikatakan telah "diratakan" . Terima kasih!
neevek
464

Stream.flatMap, karena dapat ditebak namanya, adalah kombinasi dari mapdan flatoperasi. Itu berarti bahwa Anda pertama-tama menerapkan fungsi ke elemen Anda, dan kemudian meratakannya. Stream.maphanya menerapkan fungsi ke aliran tanpa meratakan aliran.

Untuk memahami apa yang dimaksud dengan meratakan aliran, pertimbangkan struktur seperti [ [1,2,3],[4,5,6],[7,8,9] ]yang memiliki "dua tingkat". Merata ini berarti mengubahnya dalam struktur "satu tingkat": [ 1,2,3,4,5,6,7,8,9 ].

Dici
sumber
5
sederhana dan manis
bluelurker
3
Haha, jujur ​​saja saya masih terkejut melihat berapa banyak traffic yang didapat dari pertanyaan ini. Pengamatan lucu lainnya adalah bahwa sudah hampir 5 tahun saya menulis jawaban ini dan sudah ada pola peningkatan yang konsisten di mana jawaban yang diterima mendapat sekitar dua upvotes untuk setiap jawaban yang saya dapatkan. Sangat konsisten.
Dici
1
bagaimana ini bukan jawaban yang diterima, terima kasih untuk langsung ke pokok permasalahan dan memberikan contoh yang sangat sederhana
1mike12
233

Saya ingin memberikan 2 contoh untuk mendapatkan sudut pandang yang lebih praktis:
Contoh pertama menggunakan peta:

@Test
public void convertStringToUpperCaseStreams() {
    List<String> collected = Stream.of("a", "b", "hello") // Stream of String 
            .map(String::toUpperCase) // Returns a stream consisting of the results of applying the given function to the elements of this stream.
            .collect(Collectors.toList());
    assertEquals(asList("A", "B", "HELLO"), collected);
}   

Tidak ada yang istimewa pada contoh pertama, a Functionditerapkan untuk mengembalikan Stringhuruf kapital.

Contoh kedua menggunakan flatMap:

@Test
public void testflatMap() throws Exception {
    List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)) // Stream of List<Integer>
            .flatMap(List::stream)
            .map(integer -> integer + 1)
            .collect(Collectors.toList());
    assertEquals(asList(2, 3, 4, 5), together);
}

Dalam contoh kedua, Stream Daftar dilewatkan. Ini BUKAN Stream of Integer!
Jika Fungsi transformasi harus digunakan (melalui peta), maka pertama Stream harus diratakan ke sesuatu yang lain (Stream of Integer).
Jika flatMap dihapus maka kesalahan berikut dikembalikan: Operator + tidak terdefinisi untuk daftar tipe argumen, int.
TIDAK mungkin menerapkan +1 pada Daftar Integer!

Rudy Vissers
sumber
@ PrashanthDebbadwar Saya pikir Anda akan berakhir dengan Stream Stream<Integer>daripada Stream Integer.
bayar
166

Silakan melalui pos sepenuhnya untuk mendapatkan ide yang jelas,

peta vs flatMap:

Untuk mengembalikan panjang setiap kata dari daftar, kami akan melakukan sesuatu seperti di bawah ini ..

Versi pendek diberikan di bawah ini

Ketika kami mengumpulkan dua daftar, diberikan di bawah ini

Tanpa peta datar => [1,2], [1,1] => [[1,2], [1,1]] Di sini dua daftar ditempatkan di dalam daftar, sehingga hasilnya adalah daftar yang berisi daftar

Dengan peta datar => [1,2], [1,1] => [1,2,1,1] Di sini dua daftar diratakan dan hanya nilai-nilai yang ditempatkan dalam daftar, sehingga output akan menjadi daftar yang hanya berisi elemen

Pada dasarnya menggabungkan semua objek menjadi satu

## Versi terperinci telah diberikan di bawah ini: -

Misalnya: -
Pertimbangkan daftar ["STACK", "OOOVVVER"] dan kami mencoba mengembalikan daftar seperti ["STACKOVER"] (hanya mengembalikan huruf unik dari daftar itu) Awalnya, kami akan melakukan sesuatu seperti di bawah ini untuk mengembalikan sebuah daftar ["STACKOVER"] dari ["STACK", "OOOVVVER"]

public class WordMap {
  public static void main(String[] args) {
    List<String> lst = Arrays.asList("STACK","OOOVER");
    lst.stream().map(w->w.split("")).distinct().collect(Collectors.toList());
  }
}

Di sini masalahnya adalah, Lambda diteruskan ke metode peta mengembalikan array String untuk setiap kata, Jadi aliran yang dikembalikan oleh metode peta sebenarnya dari tipe Stream, Tapi yang kita butuhkan adalah Stream untuk mewakili aliran karakter, di bawah gambar menggambarkan masalah.

Gambar A:

masukkan deskripsi gambar di sini

Anda mungkin berpikir bahwa, Kita dapat menyelesaikan masalah ini menggunakan flatmap,
OK, mari kita lihat bagaimana menyelesaikannya dengan menggunakan peta dan Array.stream Pertama-tama Anda akan membutuhkan aliran karakter alih-alih aliran array. Ada metode yang disebut Arrays.stream () yang akan mengambil array dan menghasilkan aliran, misalnya:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .map(Arrays::stream).distinct() //Make array in to separate stream
    .collect(Collectors.toList());

Hal di atas masih tidak berfungsi, karena sekarang kita berakhir dengan daftar aliran (lebih tepatnya, Stream>), Alih-alih, kita harus terlebih dahulu mengonversikan setiap kata menjadi array dari setiap huruf dan kemudian membuat setiap array menjadi aliran yang terpisah

Dengan menggunakan flatMap kita harus dapat memperbaiki masalah ini seperti di bawah ini:

String[] arrayOfWords = {"STACK", "OOOVVVER"};
Stream<String> streamOfWords = Arrays.stream(arrayOfWords);
streamOfWords.map(s->s.split("")) //Converting word in to array of letters
    .flatMap(Arrays::stream).distinct() //flattens each generated stream in to a single stream
    .collect(Collectors.toList());

flatMap akan melakukan pemetaan setiap larik tidak dengan aliran tetapi dengan isi aliran itu. Semua aliran individual yang akan dihasilkan saat menggunakan peta (Array :: stream) digabung menjadi satu aliran. Gambar B menggambarkan efek dari menggunakan metode flatMap. Bandingkan dengan apa yang dilakukan peta pada gambar A. Gambar B masukkan deskripsi gambar di sini

Metode flatMap memungkinkan Anda mengganti setiap nilai aliran dengan aliran lain dan kemudian menggabungkan semua aliran yang dihasilkan menjadi satu aliran.

TechDog
sumber
2
Penjelasan diagram yang bagus.
Hitesh
108

Satu jawaban baris: flatMapmembantu meratakan Collection<Collection<T>>aCollection<T> . Dengan cara yang sama, itu juga akan meratakan Optional<Optional<T>>ke dalam Optional<T>.

masukkan deskripsi gambar di sini

Seperti yang Anda lihat, map()hanya dengan :

  • Tipe perantara adalah Stream<List<Item>>
  • Jenis kembali adalah List<List<Item>>

dan dengan flatMap():

  • Tipe perantara adalah Stream<Item>
  • Jenis kembali adalah List<Item>

Ini adalah hasil tes dari kode yang digunakan tepat di bawah ini:

-------- Without flatMap() -------------------------------
     collect() returns: [[Laptop, Phone], [Mouse, Keyboard]]

-------- With flatMap() ----------------------------------
     collect() returns: [Laptop, Phone, Mouse, Keyboard]

Kode yang digunakan :

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class Parcel {
  String name;
  List<String> items;

  public Parcel(String name, String... items) {
    this.name = name;
    this.items = Arrays.asList(items);
  }

  public List<String> getItems() {
    return items;
  }

  public static void main(String[] args) {
    Parcel amazon = new Parcel("amazon", "Laptop", "Phone");
    Parcel ebay = new Parcel("ebay", "Mouse", "Keyboard");
    List<Parcel> parcels = Arrays.asList(amazon, ebay);

    System.out.println("-------- Without flatMap() ---------------------------");
    List<List<String>> mapReturn = parcels.stream()
      .map(Parcel::getItems)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + mapReturn);

    System.out.println("\n-------- With flatMap() ------------------------------");
    List<String> flatMapReturn = parcels.stream()
      .map(Parcel::getItems)
      .flatMap(Collection::stream)
      .collect(Collectors.toList());
    System.out.println("\t collect() returns: " + flatMapReturn);
  }
}
nxhoaf
sumber
8
Contoh yang sangat tajam .., Tidak akan butuh lebih dari beberapa detik untuk memahami konsep dengan contoh Anda ...
TechDog
2
penjelasan yang bagus. sangat menghargai penjelasan sederhana dan terbaik
Sachin Rane
42

Fungsi yang Anda lewati stream.mapharus mengembalikan satu objek. Itu berarti setiap objek dalam aliran input menghasilkan tepat satu objek dalam aliran output.

Fungsi yang Anda lewati untuk stream.flatMapmengembalikan aliran untuk setiap objek. Itu berarti fungsi dapat mengembalikan sejumlah objek untuk setiap objek input (termasuk tidak ada). Aliran yang dihasilkan kemudian disatukan menjadi satu aliran keluaran.

Philipp
sumber
Mengapa Anda ingin "mengembalikan sejumlah objek untuk setiap objek input (termasuk tidak ada)"?
Derek Mahar
4
@DerekMahar Akan ada banyak kasus penggunaan untuk ini. Misalnya, katakanlah Anda memiliki aliran Departmentdi organisasi Anda. Setiap departemen memiliki antara 0 dan n Employees. Yang Anda butuhkan adalah aliran semua karyawan. Jadi apa yang kamu lakukan? Anda menulis metode flatMap yang mengambil departemen dan mengembalikan aliran karyawannya.
Philipp
Philipp, apakah contoh Anda menggambarkan alasan utama untuk menggunakan flatMap? Saya menduga itu mungkin insidentil dan tidak menggambarkan kasus penggunaan utama atau alasan mengapa flatMapada. (Lanjutan di bawah ...)
Derek Mahar
Setelah membaca dzone.com/articles/understanding-flatmap , saya pikir motivasi utama di belakang flatMapadalah untuk mengakomodasi kesalahan yang akan muncul saat menggunakan map. Bagaimana Anda menangani kasus di mana satu atau lebih item dalam set asli tidak dapat dipetakan ke item output? Dengan memperkenalkan set perantara (katakanlah Optionalatau Stream) untuk setiap objek input, flatMapmemungkinkan Anda untuk mengecualikan objek input "tidak valid" (atau yang disebut "apel buruk" dalam semangat stackoverflow.com/a/52248643/107158 ) dari set terakhir.
Derek Mahar
1
@DerekMahar Ya, stasiun di mana setiap objek input mungkin atau mungkin tidak mengembalikan objek output adalah kasus penggunaan yang baik untuk flat-map.
Philipp
29

untuk Peta kita memiliki daftar elemen dan (fungsi, tindakan) f sehingga:

[a,b,c] f(x) => [f(a),f(b),f(c)]

dan untuk peta datar kami memiliki daftar daftar elemen dan kami memiliki (fungsi, tindakan) f dan kami ingin hasilnya diratakan:

[[a,b],[c,d,e]] f(x) =>[f(a),f(b),f(c),f(d),f(e)]
Bachiri Taoufiq Abderrahman
sumber
25

Saya merasa bahwa sebagian besar jawaban di sini terlalu rumit untuk masalah sederhana. Jika Anda sudah mengerti bagaimana cara mapkerjanya itu harus cukup mudah dipahami.

Ada kasus di mana kita bisa berakhir dengan struktur bersarang yang tidak diinginkan saat menggunakan map(), flatMap()metode ini dirancang untuk mengatasinya dengan menghindari pembungkus.


Contoh:

1

List<List<Integer>> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .collect(Collectors.toList());

Kami dapat menghindari daftar bersarang dengan menggunakan flatMap:

List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3))
  .flatMap(i -> i.stream())
  .collect(Collectors.toList());

2

Optional<Optional<String>> result = Optional.of(42)
      .map(id -> findById(id));

Optional<String> result = Optional.of(42)
      .flatMap(id -> findById(id));

dimana:

private Optional<String> findById(Integer id)
Grzegorz Piwowarek
sumber
maaf, tetapi cuplikan kedua dari titik 1 tidak dapat dikompilasi List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());. SeharusnyaStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
arthur
@arthur Saya pikir saya menggunakan Stream dan Daftar Vavr di sini - tetapi saya setuju bahwa ini mungkin sedikit membingungkan - Saya akan mengubahnya ke standar Java
Grzegorz Piwowarek
@GrzegorzPiwowarek bagaimana dengan penjelasan sederhana ini ?
Eugene
22

Artikel Oracle tentang Opsional menyoroti perbedaan antara peta dan flatmap:

String version = computer.map(Computer::getSoundcard)
                  .map(Soundcard::getUSB)
                  .map(USB::getVersion)
                  .orElse("UNKNOWN");

Sayangnya, kode ini tidak dapat dikompilasi. Mengapa? Komputer variabel berjenis Optional<Computer>, sehingga sangat tepat untuk memanggil metode peta. Namun, getSoundcard () mengembalikan objek bertipe Opsional. Ini berarti hasil operasi peta adalah objek bertipe Optional<Optional<Soundcard>>. Akibatnya, panggilan ke getUSB () tidak valid karena Opsional terluar berisi nilainya sebagai Opsional lainnya, yang tentu saja tidak mendukung metode getUSB ().

Dengan stream, metode flatMap mengambil fungsi sebagai argumen, yang mengembalikan aliran lain. Fungsi ini diterapkan ke setiap elemen aliran, yang akan menghasilkan aliran aliran. Namun, flatMap memiliki efek mengganti setiap aliran yang dihasilkan oleh isi aliran itu. Dengan kata lain, semua aliran terpisah yang dihasilkan oleh fungsi digabung atau "diratakan" menjadi satu aliran tunggal. Apa yang kami inginkan di sini adalah sesuatu yang serupa, tetapi kami ingin "meratakan" dua tingkat Opsional menjadi satu .

Opsional juga mendukung metode flatMap. Tujuannya adalah untuk menerapkan fungsi transformasi pada nilai Opsional (seperti operasi peta) dan kemudian meratakan dua level Opsional menjadi satu .

Jadi, untuk membuat kode kita benar, kita perlu menulis ulang sebagai berikut menggunakan flatMap:

String version = computer.flatMap(Computer::getSoundcard)
                   .flatMap(Soundcard::getUSB)
                   .map(USB::getVersion)
                   .orElse("UNKNOWN");

FlatMap pertama memastikan bahwa Optional<Soundcard>dikembalikan bukan Optional<Optional<Soundcard>>, dan flatMap kedua mencapai tujuan yang sama untuk mengembalikan Optional<USB>. Perhatikan bahwa panggilan ketiga hanya perlu peta () karena getVersion () mengembalikan sebuah String daripada objek Opsional.

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

Rusty Core
sumber
1
pertanyaannya adalah tentang Stream.map dan Stream.flatMap & bukan tentang Optional.map anfd Optional.flatMap
djames
4
Tapi itu banyak membantu saya untuk memahami masalah saya dengan opsional dan flatmap, terima kasih banyak!
Loïc
2
@djames, ini jawaban yang benar-benar valid, bacalah mulai dari paragraf "Dengan aliran, metode flatMap mengambil fungsi sebagai argumen ..." :)
skwisgaar
Saya pikir ini adalah tambahan yang sangat berguna untuk beberapa jawaban lain di sini.
Mz A
Versi flatMap () juga melempar nullpointerexception jika soundCard nol. Jadi, di mana manfaat Optional yang dijanjikan?
Ekaterina
16

Saya tidak begitu yakin saya harus menjawab ini, tetapi setiap kali saya menghadapi seseorang yang tidak mengerti ini, saya menggunakan contoh yang sama.

Bayangkan Anda memiliki sebuah apel. A mapmentransformasikan apel itu menjadi apple-juicemisalnya atau pemetaan satu-ke-satu .

Ambil apel yang sama dan dapatkan hanya biji dari itu, itulah yang flatMapdilakukan, atau apel satu ke banyak , satu apel sebagai input, banyak biji sebagai output.

Eugene
sumber
4
Itu contoh yang menarik :)
cassiomolin
Untuk flatMapkasus ini, apakah Anda pertama-tama mengumpulkan benih dari setiap apel dalam kantong terpisah, satu kantong per apel, sebelum Anda menuangkan semua tas ke dalam satu tas?
Derek Mahar
@DerekMahar dulu miskin menjadi satu tas sebelum java-10, artinya flatmaptidak benar-benar malas, tetapi karena java-10 itu malas
Eugene
@Eugene tolong jelaskan konsep yang sedikit lebih malas yang Anda coba jelaskan tidak jelas untuk saya. Saya mengerti apa yang dijelaskan derkerMahar dalam komentar apakah itu yang terjadi sebelum java10?
JAVA
@JAVA hanya mencari flatMap + lazy, saya yakin akan ada beberapa jawaban.
Eugene
16

map () dan flatMap ()

  1. map()

Hanya membutuhkan sebuah Fungsi lambda param di mana T adalah elemen dan R elemen kembali dibangun menggunakan T. Pada akhirnya kita akan memiliki Stream dengan objek Tipe R. Contoh sederhana dapat:

Stream
  .of(1,2,3,4,5)
  .map(myInt -> "preFix_"+myInt)
  .forEach(System.out::println);

Ini hanya membutuhkan elemen 1 hingga 5 dari Tipe Integer, menggunakan setiap elemen untuk membangun elemen baru dari tipe Stringdengan nilai "prefix_"+integer_valuedan mencetaknya.

  1. flatMap()

Sangat berguna untuk mengetahui bahwa flatMap () mengambil fungsi di F<T, R>mana

  • T adalah jenis dari mana Stream dapat dibangun dari / dengan . Ini bisa berupa Daftar (T.stream ()), array (Arrays.stream (someArray)), dll. Apa pun yang darinya Aliran dapat dengan / atau bentuk. dalam contoh di bawah ini setiap dev memiliki banyak bahasa, jadi dev. Bahasa adalah Daftar dan akan menggunakan parameter lambda.

  • R adalah Aliran yang dihasilkan yang akan dibangun menggunakan T. Mengetahui bahwa kita memiliki banyak contoh T, kita secara alami akan memiliki banyak Aliran dari R. Semua Aliran ini dari Tipe R sekarang akan digabungkan menjadi satu Aliran 'flat' tunggal dari Tipe R .

Contoh

Contoh-contoh Bachiri Taoufiq lihat jawabannya di sini sederhana dan mudah dimengerti. Hanya untuk kejelasan, katakan saja kami memiliki tim pengembang:

dev_team = {dev_1,dev_2,dev_3}

, dengan setiap pengembang mengetahui banyak bahasa:

dev_1 = {lang_a,lang_b,lang_c},
dev_2 = {lang_d},
dev_2 = {lang_e,lang_f}

Menerapkan Stream.map () di dev_team untuk mendapatkan bahasa dari masing-masing dev:

dev_team.map(dev -> dev.getLanguages())

akan memberi Anda struktur ini:

{ 
  {lang_a,lang_b,lang_c},
  {lang_d},
  {lang_e,lang_f}
}

yang pada dasarnya adalah a List<List<Languages>> /Object[Languages[]]. Tidak terlalu cantik, tidak seperti Java8 !!

dengan Stream.flatMap()Anda dapat 'meratakan' hal-hal karena mengambil struktur di atas
dan mengubahnya menjadi {lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}, yang pada dasarnya dapat digunakan sebagai List<Languages>/Language[]/etc...

jadi pada akhirnya, kode Anda akan lebih masuk akal seperti ini:

dev_team
   .stream()    /* {dev_1,dev_2,dev_3} */
   .map(dev -> dev.getLanguages()) /* {{lang_a,...,lang_c},{lang_d}{lang_e,lang_f}}} */
   .flatMap(languages ->  languages.stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
   .doWhateverWithYourNewStreamHere();

atau hanya:

dev_team
       .stream()    /* {dev_1,dev_2,dev_3} */
       .flatMap(dev -> dev.getLanguages().stream()) /* {lang_a,...,lang_d, lang_e, lang_f} */
       .doWhateverWithYourNewStreamHere();

Kapan menggunakan peta () dan menggunakan flatMap () :

  • Gunakan map()ketika setiap elemen tipe T dari aliran Anda seharusnya dipetakan / diubah menjadi elemen tunggal tipe R. Hasilnya adalah pemetaan tipe (1 elemen awal -> 1 elemen ujung) dan aliran baru elemen tipe R dikembalikan.

  • Gunakan flatMap()ketika setiap elemen tipe T dari aliran Anda seharusnya dipetakan / diubah menjadi Koleksi elemen tipe R. Hasilnya adalah pemetaan tipe (1 elemen awal -> n elemen ujung) . Koleksi ini kemudian digabung (atau diratakan ) ke aliran baru elemen tipe R. Ini berguna misalnya untuk mewakili loop bersarang .

Pra Jawa 8:

List<Foo> myFoos = new ArrayList<Foo>();
    for(Foo foo: myFoos){
        for(Bar bar:  foo.getMyBars()){
            System.out.println(bar.getMyName());
        }
    }

Pos Jawa 8

myFoos
    .stream()
    .flatMap(foo -> foo.getMyBars().stream())
    .forEach(bar -> System.out.println(bar.getMyName()));
arthur
sumber
11

Peta: - Metode ini mengambil satu Fungsi sebagai argumen dan mengembalikan aliran baru yang terdiri dari hasil yang dihasilkan dengan menerapkan fungsi yang diteruskan ke semua elemen aliran.

Mari kita bayangkan, saya memiliki daftar nilai integer (1,2,3,4,5) dan satu antarmuka fungsi yang logikanya adalah kuadrat dari bilangan bulat yang dilewati. (e -> e * e).

List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);

List<Integer> newList = intList.stream().map( e -> e * e ).collect(Collectors.toList());

System.out.println(newList);

keluaran:-

[1, 4, 9, 16, 25]

Seperti yang Anda lihat, output adalah aliran baru yang nilainya kuadrat dari nilai aliran input.

[1, 2, 3, 4, 5] -> apply e -> e * e -> [ 1*1, 2*2, 3*3, 4*4, 5*5 ] -> [1, 4, 9, 16, 25 ]

http://codedestine.com/java-8-stream-map-method/

FlatMap: - Metode ini mengambil satu Fungsi sebagai argumen, fungsi ini menerima satu parameter T sebagai argumen input dan mengembalikan satu aliran parameter R sebagai nilai balik. Ketika fungsi ini diterapkan ke setiap elemen aliran ini, itu menghasilkan aliran nilai-nilai baru. Semua elemen aliran baru yang dihasilkan oleh setiap elemen kemudian disalin ke aliran baru, yang akan menjadi nilai balik dari metode ini.

Mari kita gambar, saya memiliki daftar objek siswa, di mana setiap siswa dapat memilih beberapa mata pelajaran.

List<Student> studentList = new ArrayList<Student>();

  studentList.add(new Student("Robert","5st grade", Arrays.asList(new String[]{"history","math","geography"})));
  studentList.add(new Student("Martin","8st grade", Arrays.asList(new String[]{"economics","biology"})));
  studentList.add(new Student("Robert","9st grade", Arrays.asList(new String[]{"science","math"})));

  Set<Student> courses = studentList.stream().flatMap( e -> e.getCourse().stream()).collect(Collectors.toSet());

  System.out.println(courses);

keluaran:-

[economics, biology, geography, science, history, math]

Seperti yang Anda lihat, output adalah aliran baru yang nilainya merupakan kumpulan dari semua elemen aliran yang dikembalikan oleh setiap elemen aliran input.

[S1, S2, S3] -> [{"sejarah", "matematika", "geografi"}, {"ekonomi", "biologi"}, {"sains", "matematika"}] -> ambil mata pelajaran unik - > [ekonomi, biologi, geografi, sains, sejarah, matematika]

http://codedestine.com/java-8-stream-flatmap-method/

lalitbhagtani
sumber
bisa membuat perbedaan jika Anda memberikan kode alih-alih hanya memprovokasi tautan doc
Charles-Antoine Fournel
11

.map untuk pemetaan A -> B

Stream.of("dog", "cat")              // stream of 2 Strings
    .map(s -> s.length())            // stream of 2 Integers: [3, 3]

itu mengkonversi item Aapa saja menjadi item apa saja B. Javadoc


.flatMap untuk A -> Stream <B> menggabungkan

Stream.of("dog", "cat")             // stream of 2 Strings
    .flatMapToInt(s -> s.chars())   // stream of 6 ints:      [d, o, g, c, a, t]

--1 mengonversi item apa saja Amenjadi Stream< B>, kemudian --2 menggabungkan semua aliran menjadi satu aliran (datar). Javadoc


Catatan 1: Meskipun contoh terakhir flat ke aliran primitif (IntStream), bukan aliran objek (Stream), itu masih menggambarkan ide dari .flatMap.

Catatan 2: Terlepas dari namanya, metode String.chars () mengembalikan int. Jadi koleksi aktualnya adalah:, di [100, 111, 103, 99, 97, 116] mana 100kodenya 'd', 111adalah kodenya 'o'dll. Sekali lagi, untuk tujuan ilustrasi, itu disajikan sebagai [d, o, g, c, a, t].

epox
sumber
3
Jawaban Terbaik. Langsung ke titik dengan contoh
GabrielBB
1

Jawaban sederhana

The mapoperasi dapat menghasilkan Streamdari Stream.exStream<Stream<Integer>>

flatMapoperasi hanya akan menghasilkan Streamsesuatu. EXStream<Integer>

Basilika Melad
sumber
0

Analogi yang bagus juga bisa dengan C # jika Anda terbiasa. Pada dasarnya C # Selectmirip dengan java mapdan C # SelectManyjava flatMap. Hal yang sama berlaku untuk Kotlin untuk koleksi.

Arsenius
sumber
0

Ini sangat membingungkan bagi pemula. Perbedaan dasarnya adalah mapmemancarkan satu item untuk setiap entri dalam daftar dan flatMappada dasarnya adalah operasi map+ flatten. Agar lebih jelas, gunakan flatMap ketika Anda membutuhkan lebih dari satu nilai, misalnya ketika Anda mengharapkan loop untuk mengembalikan array, flatMap akan sangat membantu dalam hal ini.

Saya telah menulis blog tentang ini, Anda dapat memeriksanya di sini .

Niraj Chauhan
sumber
0

Alirkan operasi flatMapdan mapterima fungsi sebagai input.

flatMapmengharapkan fungsi untuk mengembalikan aliran baru untuk setiap elemen aliran dan mengembalikan aliran yang menggabungkan semua elemen aliran yang dikembalikan oleh fungsi untuk setiap elemen. Dengan kata lain, dengan flatMap, untuk setiap elemen dari sumber, beberapa elemen akan dibuat oleh fungsi. http://www.zoftino.com/java-stream-examples#flatmap-operation

mapmengharapkan fungsi untuk mengembalikan nilai yang diubah dan mengembalikan aliran baru yang mengandung elemen yang diubah. Dengan kata lain, dengan map, untuk setiap elemen dari sumber, satu elemen yang diubah akan dibuat oleh fungsi. http://www.zoftino.com/java-stream-examples#map-operation

Arnav Rao
sumber
0

flatMap()juga mengambil keuntungan dari evaluasi aliran malas sebagian. Ini akan membaca aliran pertama dan hanya jika diperlukan, akan pergi ke aliran berikutnya. Perilaku ini dijelaskan secara rinci di sini: Apakah flatMap dijamin malas?

SK
sumber
0

Jika Anda berpikir map()sebagai iterasi (satu tingkat forloop), flatmap()adalah iterasi dua tingkat (seperti forloop bersarang ). (Masukkan setiap elemen yang diulang foo, dan lakukan foo.getBarList()dan ulangi di dalamnya barListlagi)


map(): ambil aliran, lakukan sesuatu untuk setiap elemen, kumpulkan hasil tunggal dari setiap proses, keluaran aliran lain. Definisi "melakukan sesuatu fungsi" adalah implisit. Jika proses dari setiap elemen menghasilkan null, nulldigunakan untuk menyusun aliran akhir. Jadi, jumlah elemen dalam aliran yang dihasilkan akan sama dengan jumlah aliran input.

flatmap(): ambil aliran elemen / aliran dan fungsi (definisi eksplisit), terapkan fungsi ke setiap elemen dari setiap aliran, dan kumpulkan semua aliran hasil antara menjadi aliran yang lebih besar ("perataan"). Jika proses dari setiap elemen menghasilkan null, aliran kosong disediakan untuk langkah terakhir "perataan". Jumlah elemen dalam aliran yang dihasilkan, adalah total dari semua elemen yang berpartisipasi dalam semua input, jika input beberapa aliran.

WesternGun
sumber