Java 8'de map () ve flatMap () yöntemleri arasındaki fark nedir?


709

Java 8'de, yöntemler Stream.map()ile Stream.flatMap()yöntemler arasındaki fark nedir?


56
Tip imzası tüm hikayeyi anlatıyor. map :: Stream T -> (T -> R) -> Stream R, flatMap :: Stream T -> (T -> Stream R) -> Stream R.
Chris Martin

99
fwiw, bu tür imzalar Java'ya bile benzemiyor. (Biliyorum, biliyorum - ama söylemek için "tüm hikaye" wrt haritası / flatMap yeni & geliştirilmiş "Java ++" hakkında çok fazla bilgi varsayar)
michael

16
@michael Bu tür imza Java'ya değil, Haskell'e benziyor. Ama asıl Java imza artık okunabilir olup olmadığı net değil: <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper).
Stuart Marks

8
Ha, evet, "gerçek Java" ya atıfta bulunuyordum. C ++ gibi, modern Java da 90'larda kullanmaya başlayan herkes tarafından neredeyse tanınmaz (benim yaptığım gibi, her iki dilde). Sadece yoruma yanıt olarak, bu yöntem imzaları, ek açıklama olmaksızın (veya yorum yapanların durumunda, çeviri) en azından artık bir "tüm hikayeyi" anlatmakla kalmaz.
michael

2
Bir, demek ki hangisi map'ın mapper lambda döner R, bir flatMapbir' ler mapper lamda döner Streamait R( Stream<R>). flatMap'S haritacısı tarafından döndürülen akışlar etkin bir şekilde birleştirilir. Aksi takdirde, her ikisi de mapve flatMapgeri Stream<R>; Fark mapper lambda'lar dönmek budur Rvs Stream<R>.
derekm

Yanıtlar:


817

Hem mapve flatMapbir uygulanabilir Stream<T>ve dönüşü her ikisi de Stream<R>. Fark, mapişlemin her giriş değeri için bir çıkış değeri üretmesidir, oysa flatMapişlem her giriş değeri için rasgele bir sayı (sıfır veya daha fazla) değeri üretir.

Bu her bir operasyonun argümanlarına yansır.

İşlem , giriş akışındaki her değer için çağrılan mapa'yı alır Functionve çıkış akışına gönderilen bir sonuç değeri üretir.

İşlem flatMap, kavramsal olarak bir değeri tüketmek ve isteğe bağlı sayıda değer üretmek isteyen bir işlevi alır. Bununla birlikte, Java'da, yöntemlerin yalnızca sıfır veya bir değer döndüğü için rasgele sayıda değer döndürmesi bir yöntem için zahmetlidir. Haritacı işlevinin flatMapbir değer aldığı ve bir dizi veyaListdaha sonra çıktıya gönderilir. Bunun akış kütüphanesi olduğu düşünüldüğünde, rastgele sayıda dönüş değerini temsil etmenin özellikle uygun bir yolu, haritacı işlevinin kendisinin bir akış döndürmesidir! Eşleyici tarafından döndürülen akıştan alınan değerler akıştan boşaltılır ve çıkış akışına geçirilir. Eşleyici işlevine yapılan her çağrı tarafından döndürülen değerlerin "kümeleri", çıkış akışında hiç ayırt edilmez, dolayısıyla çıktının "düzleştirildiği" söylenir.

Tipik kullanım, sıfır değerleri göndermek istiyorsa veya birkaç değer döndürmek istiyorsa gibi bir şey flatMapdöndürmek için eşleyici işlevinin kullanılmasıdır . Ancak elbette herhangi bir akış döndürülebilir.Stream.empty()Stream.of(a, b, c)


26
Bana flatMapameliyat gibi geliyor dairenin tam tersi. Yine, kafasındaki bir terimi çevirmek için Bilgisayar Bilimcilerine bırakın. Bir fonksiyonun "saydam" olması gibi, herhangi bir şeyi göremeyeceğiniz anlamına gelir, sadece sonuçları görürken, bir sürecin saydam olmasını istediğinizi söylemek, her parçasının görülmesini istediğiniz anlamına gelir.
coladict

45
@coladict Onu farklı bir perspektiften görüntülemeyi deneyin: bu, iç işleyişleri görebileceğiniz şeffaf bir durum değil, ancak tüm işlevlerin kendisi sizin için şeffaf, yani görünmez - hala işlerini yaparken ve ne yaptığınızı görmenize izin verirken ile çalışıyoruz. Bu durumda, "düz" "iç içe" nin tersini belirtir, flatmap düzleştirme yoluyla bir iç içe geçme seviyesini kaldırır.
Zefiro

7
@coladict "Şeffaf" şey yıllardır kafamı yiyor. En az bir kişinin aynı şekilde hissettiğini bilmekten memnunum.
Ashok Bijoy Debnath

9
Düzleştirme, 2 seviyeli yapıyı tek seviyeli bir yapıya dönüştürmekten gelir, örnek için Dici'nin cevabına bakınız stackoverflow.com/a/26684582/6012102
andrzej.szmukala

26
Bu flatMap'in en iyi açıklamasıdır . Hepsini tıklayan da budur: Eşleyici tarafından döndürülen akıştan gelen değerler akıştan boşaltılır ve çıkış akışına geçirilir. Eşleyici işlevine yapılan her çağrı tarafından döndürülen değerlerin "kümeleri", çıkış akışında hiç ayırt edilmez, bu nedenle çıktının "düzleştirilmiş" olduğu söylenir . Teşekkür ederim!
neevek

465

Stream.flatMap, adından da anlaşılacağı gibi, a mapve flatişlemin birleşimidir . Bu, önce öğelerinize bir işlev uyguladığınız ve daha sonra düzleştirdiğiniz anlamına gelir. Stream.mapakışı düzleştirmeden yalnızca akışa bir işlev uygular.

Bir akışın hangi düzleştirmenin[ [1,2,3],[4,5,6],[7,8,9] ] içerdiğini anlamak için "iki seviyeye" sahip bir yapı düşünün . "Tek düzeyi" yapısında dönüştürerek bu araçları düzleştirme: [ 1,2,3,4,5,6,7,8,9 ].


6
basit ve tatlı
bluelurker

3
Haha, adil olmak gerekirse, bu sorunun ne kadar trafik aldığını görmek beni hala şaşırttı. Bir başka komik gözlem ise, bu cevabı yazdığım neredeyse 5 yıl oldu ve kabul edilen cevabın, cevabımın aldığı her biri için kabaca iki yukarı oy aldığı oldukça tutarlı bir upvoting modeli var. Şaşırtıcı derecede tutarlı.
Dici

1
kabul edilen cevap nasıl değil, doğrudan
konuya geldiğiniz

234

Daha pratik bir bakış açısı elde etmek için 2 örnek vermek istiyorum :
Haritadan yararlanan ilk örnek:

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

İlk örnekte özel bir şey olmayan a Function, Stringbüyük harfe dönmek için uygulanır .

İkinci örnek 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);
}

İkinci örnekte, bir Liste Akışı geçirilmiştir. Bir Tamsayı Akışı DEĞİL!
Bir dönüşüm İşlevinin kullanılması gerekiyorsa (harita üzerinden), önce Akış başka bir şeye (Tamsayı Akışı) düzleştirilmelidir.
FlatMap kaldırılırsa, aşağıdaki hata döndürülür: + işleci, bağımsız değişken türleri Listesi, int için tanımsızdır.
Tam Sayılar Listesine + 1 uygulamak mümkün DEĞİLDİR!


@PrashanthDebbadwar Bence bir Akış Stream<Integer>yerine bir Akış ile sona ereceğini düşünüyorum Integer.
Payne

166

Açık bir fikir edinmek için lütfen yazıyı tamamen gözden geçirin,

map vs flatMap:

Listeden her kelimenin uzunluğunu döndürmek için aşağıdaki gibi bir şey yaparız.

Kısa Versiyon aşağıda verilmiştir

Aşağıda verilen iki liste topladığımızda

Düz harita olmadan => [1,2], [1,1] => [[1,2], [1,1]] Burada iki liste bir listenin içine yerleştirilir, böylece çıktı listeler içeren liste olacaktır

İle düz harita => [1,2], [1,1] => [1,2,1,1] Burada iki liste düzleştirilir ve çıkış sadece elemanları içeren liste olacak şekilde yalnızca değerleri, listeye yerleştirilir

Temelde tüm nesneleri bir araya getirir

## Ayrıntılı Versiyon aşağıda verilmiştir: -

Örneğin: -
Bir liste [“STACK”, ”OOOVVVER”] düşünün ve [“STACKOVER”] gibi bir liste döndürmeye çalışıyoruz (bu listeden yalnızca benzersiz harfler döndürerek) Başlangıçta, [“STACK”, ”OOOVVVER” den [“STACKOVER”] listesini

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

Burada sorun, Lambda, harita yöntemine geçirilen her sözcük için bir String dizisi döndürüyor. sorun.

Şekil A:

resim açıklamasını buraya girin

Bu sorunu flatmap kullanarak çözebiliriz,
tamam, bunu map ve Arrays kullanarak nasıl çözeceğimizi görelim. Akış Öncelikle diziler yerine bir karakter akışına ihtiyacınız olacak. Arrays.stream () adında bir dizi alan ve bir akış oluşturan bir yöntem vardır, örneğin:

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

Yukarıdakiler hala çalışmıyor, çünkü şimdi bir akış listesi (daha kesin olarak, Akış>) ile sonuçlanıyoruz, Bunun yerine, her kelimeyi önce tek tek harflerden oluşan bir diziye dönüştürmeli ve sonra her diziyi ayrı bir akışa dönüştürmeliyiz

FlatMap kullanarak bu sorunu aşağıdaki gibi çözebilmeliyiz:

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 her diziyi akışla değil, o akışın içeriğiyle eşleştirir. Harita (Arrays :: stream) kullanılırken oluşturulacak tüm akışlar tek bir akışta birleştirilir. Şekil B, flatMap yönteminin kullanılmasının etkisini göstermektedir. Harita şekil A. ne yaptığını ile kıyaslayın Şekil B resim açıklamasını buraya girin

FlatMap yöntemi, bir akışın her bir değerini başka bir akışla değiştirmenize olanak tanır ve ardından oluşturulan tüm akışları tek bir akışa birleştirir.


2
Güzel diyagramatik açıklama.
Hitesh

109

Bir satır cevap: flatMapa'nın Collection<Collection<T>>aCollection<T> . Aynı şekilde, aynı zamanda bir dümdüz olacak Optional<Optional<T>>INTO Optional<T>.

resim açıklamasını buraya girin

Gördüğünüz gibi, map()yalnızca:

  • Ara tip Stream<List<Item>>
  • Dönüş türü: List<List<Item>>

ve flatMap():

  • Ara tip Stream<Item>
  • Dönüş türü: List<Item>

Bu, aşağıda kullanılan kodun test sonucudur :

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

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

Kullanılan kod :

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

8
Çok net bir örnek .., Örneği ile kavramı anlamak için birkaç saniye daha fazla
sürmez

2
güzel bir açıklama. basit ve en iyi açıklama için gerçekten takdir
Sachin Rane

42

Geçtiğiniz işlev stream.map bir nesne döndürmesi gerekir. Bu, giriş akışındaki her bir nesnenin çıkış akışında tam olarak bir nesne ile sonuçlandığı anlamına gelir.

Geçtiğiniz işlev stream.flatMap, her nesne için bir akış döndürür. Bu, işlevin her giriş nesnesi için (hiçbiri dahil) istediğiniz sayıda nesne döndürebileceği anlamına gelir. Elde edilen akışlar daha sonra bir çıkış akışına birleştirilir.


Neden "her girdi nesnesi için (hiçbiri dahil) herhangi bir sayıda nesne döndürmek istiyorsunuz?"
Derek Mahar

4
@DerekMahar Bunun için birçok kullanım durumu olurdu. Örneğin, Departmentkuruluşunuzda bir s akışınız olduğunu varsayalım. Her bölüm 0 ile n Employees arasındadır . İhtiyacınız olan şey tüm çalışanların akışıdır. Ee ne yapıyorsun? Bir departmanı alan ve çalışanlarının akışını döndüren bir flatMap yöntemi yazıyorsunuz.
Philipp

Philipp, örneğiniz kullanımın ana nedenini gösteriyor flatMapmu? Bunun tesadüfi olabileceğinden ve temel kullanım durumunu ya da var olma nedenini göstermediğinden şüpheleniyorum flatMap. (Aşağıda devam ediyor ...)
Derek Mahar

Dzone.com/articles/understanding-flatmap okuduktan sonra , arkasındaki ana motivasyon flatMapkullanırken kullanılacak hataları barındırmak olduğunu düşünüyorum map. Orijinal kümedeki bir veya daha fazla öğenin bir çıktı öğesiyle eşlenemediği durumları nasıl ele alırsınız? Her giriş nesnesi için bir ara küme (bir Optionalveya diyelim Stream) flatMapekleyerek, "geçersiz" giriş nesnelerini (veya "kötü elmalar" olarak adlandırılan ) stackoverflow.com/a/52248643/107158 son küme.
Derek Mahar

1
@DerekMahar Evet, her girdi nesnesinin bir çıktı nesnesini döndürüp döndüremeyeceği durumlar, düz harita için başka bir iyi kullanım durumudur.
Philipp

29

Bir Harita için elemanların bir listesi ve bir (fonksiyon, eylem) f vardır:

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

ve düz harita için bir öğe listesi var ve bir (fonksiyon, eylem) f var ve sonucun düzleştirilmesini istiyoruz:

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

25

Buradaki yanıtların çoğunun basit sorunu aşırı derecede karmaşık hale getirdiğini hissediyorum. Zaten nasıl olduğunu anlıyorsanızmap işlerin .

Kullanırken istenmeyen yuvalanmış yapılarla sonuçlanabileceğimiz durumlar vardır map(), flatMap()yöntem sarmadan kaçınarak bunun üstesinden gelmek için tasarlanmıştır.


Örnekler:

1

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

Yuvalanmış listeler kullanmaktan kaçınabiliriz 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));

nerede:

private Optional<String> findById(Integer id)

üzgünüm ama nokta 1'deki 2. pasaj bunun yerine derlenmiyor List<Integer> result = Stream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(i -> i) .collect(Collectors.toList());. O olmalı olduğuStream.of(Arrays.asList(1), Arrays.asList(2, 3)) .flatMap(List::stream) .collect(Collectors.toList());
arthur

@arthur Sanırım Vavr'ın Akışını ve Listesini burada kullandım - ama biraz kafa karıştırıcı olabileceğini kabul ediyorum - bunu standart Java'ya değiştireceğim
Grzegorz Piwowarek


22

Oracle'ın İsteğe Bağlı makalesi, harita ve flatmap arasındaki bu farkı vurgular:

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

Ne yazık ki, bu kod derlenmiyor. Neden? Değişken bilgisayar tiptedir Optional<Computer>, bu nedenle harita yöntemini çağırmak tamamen doğrudur. Ancak, getSoundcard () yöntemi İsteğe bağlı türünde bir nesne döndürür. Bu, harita işleminin sonucunun tür bir nesne olduğu anlamına gelirOptional<Optional<Soundcard>> . Sonuç olarak, getUSB () çağrısı geçersizdir, çünkü en dıştaki İsteğe bağlı olarak başka bir İsteğe bağlı değer içerir, bu da getUSB () yöntemini desteklemez.

Akışlarda flatMap yöntemi, başka bir akış döndüren bir işlevi bağımsız değişken olarak alır. Bu işlev, bir akış akışıyla sonuçlanacak bir akışın her öğesine uygulanır. Bununla birlikte, flatMap, oluşturulan her akışı, o akışın içeriğiyle değiştirme etkisine sahiptir. Başka bir deyişle, fonksiyon tarafından üretilen tüm ayrı akışlar tek bir akışta birleştirilir veya "düzleştirilir". Burada istediğimiz benzer bir şey, ancak iki seviyeli bir İsteğe bağlı olanı "düzleştirmek" istiyoruz .

İsteğe bağlı bir flatMap yöntemini de destekler. Amacı, dönüştürme işlevini bir İsteğe bağlı değerine (harita işleminin yaptığı gibi) uygulamak ve sonra elde edilen iki düzeyli İsteğe Bağlı'yı tek bir düzeye düzleştirmektir .

Bu nedenle, kodumuzu doğru hale getirmek için flatMap kullanarak aşağıdaki gibi yeniden yazmamız gerekir:

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

İlk flatMap Optional<Soundcard>, a yerine a döndürülmesini sağlar Optional<Optional<Soundcard>>ve ikinci flatMap, bir döndürmek için aynı amacı yerine getirir Optional<USB>. GetVersion () öğesi İsteğe bağlı bir nesne yerine bir String döndürdüğü için üçüncü aramanın yalnızca bir harita () olması gerektiğini unutmayın.

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


1
soru Stream.map ve Stream.flatMap hakkındaydı ve Optional.map anfd ile ilgili değildi Optional.flatMap
djames

4
Ama isteğe bağlı ve flatmap ile sorunlarımı anlamama çok yardımcı oldu, çok teşekkürler!
Loïc

2
@djames, bu tamamen geçerli bir cevap, :) "... flatMap yöntemi bir argüman olarak bir işlev alır akışları ile" o paragrafın başlayarak okuma
skwisgaar

Buradaki diğer cevapların bazılarına çok faydalı bir katkı olduğunu düşünüyorum.
Mz A

soundCard boşsa flatMap () sürümü de nullpointerexception atar. Peki Opsiyonel'in vaat edilen faydası nerede?
Ekaterina

16

Buna cevap vereceğimden pek emin değilim, ama bunu anlamayan biriyle her karşılaştığımda aynı örneği kullanıyorum.

Bir elmanın olduğunu hayal et. A mapbu elmayı apple-juiceörneğin veya bire bir haritalamaya dönüştürüyor.

Ne olduğunu, aynı elma alın ve bunun dışında sadece tohum almak flatMapyapar ya da bir çoğuna tek çıkış olarak girdi, birçok tohum olarak, bir elma.


4
Bu ilginç bir örnek :)
cassiomolin

Bu flatMapdurumda, tüm poşetleri tek bir torbaya dökmeden önce, her elmadan gelen tohumları ayrı bir çantada, elma başına bir torbada mı topluyorsunuz?
Derek Mahar

@DerekMahar, java-10'dan önce tek bir torbaya fakirdi, yani flatmapgerçekten tembel değildi, ama java-10'dan beri tembel
Eugene

@Eugene lütfen bana açık olmayan açıklamaya çalıştığınız biraz daha tembel bir kavramı açıklayın. DerkerMahar'ın yorumda ne açıkladığını anladım java10'dan önce ne olduğu?
JAVA

@ JAVA sadece arama flatMap + lazy, bazı cevaplar olacak eminim.
Eugene

16

map () ve flatMap ()

  1. map()

Sadece bir Fonksiyonu alır, burada T elemanıdır ve R, T kullanılarak inşa edilen dönüş elemanıdır. Sonunda, R Tipi nesnelerle bir Akışımız olacaktır. Basit bir örnek şöyle olabilir:

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

Sadece Tip 1 ila 5 elemanlarını alır Integer , her öğeyi Stringdeğer içeren türden yeni bir öğe oluşturmak için kullanır "prefix_"+integer_valueve yazdırır.

  1. flatMap()

FlatMap () işlevinin aşağıdaki F<T, R>durumlarda bir işlev aldığını bilmek yararlıdır:

  • T, bir Stream'in içinden / ile oluşturulabildiği bir tiptir . Bu bir List (T.stream ()), bir dizi (Arrays.stream (someArray)), vb. Olabilir. aşağıdaki örnekte her geliştiricinin birçok dili vardır, bu yüzden dev. Diller bir Listedir ve lambda parametresini kullanır.

  • R, T kullanılarak oluşturulacak olan Akımdır. Birçok T örneğine sahip olduğumuzu bilerek, doğal olarak R'den çok sayıda Akışımız olacaktır. R Tipi'nden tüm bu Akışlar artık tek birleştirilebilir tek bir 'düz' Type R dan Akış .

Misal

Bachiri Taoufiq örnekleri buradaki cevabının örnekleri basit ve anlaşılması kolaydır. Sadece netlik için, geliştiricilerden oluşan bir ekibimiz olduğunu varsayalım:

dev_team = {dev_1,dev_2,dev_3}

, her geliştiricinin birçok dil bilmesi ile:

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

Başvuru Stream.map () dev_team her dev dillerini almak için:

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

size bu yapıyı verecektir:

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

temelde bir List<List<Languages>> /Object[Languages[]] . Çok hoş değil, ne de Java8 gibi !!

ile Stream.flatMap()yukarıdaki yapıyı alır olarak sizin şeyler 'düzleştirmek' olabilir
ve haline döner{lang_a, lang_b, lang_c, lang_d, lang_e, lang_f} temelde olarak kullanılan olabilir, List<Languages>/Language[]/etc...

böylece sonunda, kodunuz daha anlamlı olur:

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

ya da sadece:

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

Map () ve flatMap () ne zaman kullanılır? :

  • Kullanım map()için akımından T türü her elemanı bir dönüştürülmüş / eşlenmesi için beklenen tek tip R. elemanının sonuç türü bir eşleme (1 eleman başlangıç -> 1 bitiş öğesi) ve R tipi elemanlarının yeni akım Iade edildi.

  • Kullanım flatMap()akışınızdan tipi T her öğe eşlenen / a dönüştürülmesi gerektiği zaman Koleksiyonları türünün tür R. sonucu elemanlarının bir haritalama (1 başlangıç elemanı -> n uç elemanları) . Bu Koleksiyonlar daha sonra R tipi yeni bir eleman akışıyla birleştirilir (veya düzleştirilir ). Bu, örneğin iç içe halkaları temsil etmek için yararlıdır .

Java 8 Öncesi:

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

Java 8 yayınla

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

11

Harita: - Bu yöntem, bir Fonksiyonu bağımsız değişken olarak alır ve aktarılan işlev akışın tüm öğelerine uygulanarak oluşturulan sonuçlardan oluşan yeni bir akış döndürür.

Bir tamsayı değerleri (1,2,3,4,5) ve mantığı geçirilen tamsayının karesi olan bir işlev arabirimine sahip olduğumu düşünelim. (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);

çıktı:-

[1, 4, 9, 16, 25]

Gördüğünüz gibi bir çıktı, değerleri giriş akışının değerlerinin karesi olan yeni bir akıştır.

[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: - Bu yöntem bir Fonksiyonu bağımsız değişken olarak alır, bu fonksiyon bir parametre T'yi giriş bağımsız değişkeni olarak kabul eder ve bir parametre R akışını bir dönüş değeri olarak döndürür. Bu işlev, bu akışın her bir öğesine uygulandığında, yeni değerler akışı üretir. Her bir eleman tarafından üretilen bu yeni akışların tüm elemanları daha sonra bu yöntemin bir dönüş değeri olacak yeni bir akıma kopyalanır.

Her öğrencinin birden fazla konuyu seçebileceği bir öğrenci nesneleri listem var.

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

çıktı:-

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

Gördüğünüz gibi bir çıktı, değerleri girdi akışının her bir elemanı tarafından döndürülen akışların tüm öğelerinin bir toplamı olan yeni bir akıştır.

[S1, S2, S3] -> [{"tarih", "matematik", "coğrafya"}, {"ekonomi", "biyoloji"}, {"bilim", "matematik"}] -> benzersiz konular ele alın - > [ekonomi, biyoloji, coğrafya, bilim, tarih, matematik]

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


sadece doc bağlantısını
provoke etmek

11

.map içindir > B - A haritalama

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

herhangi bir öğeyi dönüştüren Aherhangi bir öğenin B. Javadoc


.flatMap içindir A -> Akım <B> concatinating

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

--1 herhangi bir öğeyi Adönüştürür Stream< B>, sonra --2 tüm akışları bir (düz) akışa birleştirir. Javadoc


Not 1: Her ne kadar son örnek, bir nesne akışı (Akış) yerine bir ilkel akışına (IntStream) yaslansa da, hala .flatMap.

Not 2: Ada rağmen String.chars () yöntemi ints döndürür. Gerçek toplama olacak şekilde: [100, 111, 103, 99, 97, 116] , burada 100kod olan 'd', 111bir kodu 'o'vs. Yine, açıklama amacıyla, bu [d, o, g, c, A, T] olarak sunulmuştur var.


3
En iyi cevap. Doğrudan örneklerle noktaya
GabrielBB

1

Basit cevap.

mapİşlem üretebilir Streamarasında Stream.EXStream<Stream<Integer>>

flatMapoperasyon sadece bir Streamşey üretir . EXStream<Integer>


0

Eğer aşina iseniz da C # ile iyi bir benzetme olabilir. Temelde C # Selectjava mapve C # SelectManyjava benzer flatMap. Aynı şey koleksiyonlar için Kotlin için de geçerlidir.


0

Bu yeni başlayanlar için çok kafa karıştırıcı. Temel fark, maplistedeki her giriş için bir öğe yayar ve flatMaptemel olarak bir map+ flattenişlemidir. Daha açık olmak gerekirse, birden fazla değere ihtiyacınız olduğunda flatMap kullanın, örneğin bir döngünün dizileri döndürmesini beklediğinizde, flatMap bu durumda gerçekten yardımcı olacaktır.

Bu konuda bir blog yazdım, sen bunu kontrol edebilirsiniz burada .



0

İşlemleri aktarın flatMapve mapbir işlevi girdi olarak kabul edin.

flatMapişlevin, akışın her öğesi için yeni bir akış döndürmesini bekler ve her öğenin işlevi tarafından döndürülen akışların tüm öğelerini birleştiren bir akış döndürür. Başka bir deyişle, flatMapkaynaktaki her öğe için, işlev tarafından birden çok öğe oluşturulur. http://www.zoftino.com/java-stream-examples#flatmap-operation

mapişlevin dönüştürülmüş bir değer döndürmesini bekler ve dönüştürülen öğeleri içeren yeni bir akış döndürür. Başka bir deyişle, mapkaynaktaki her bir öğe için, işlev tarafından bir dönüştürülmüş öğe oluşturulur. http://www.zoftino.com/java-stream-examples#map-operation



0

map()Yineleme (bir düzey fordöngü) olarak düşünüyorsanız , flatmap()iki düzeyli bir yinelemedir (iç içe fordöngü gibi ). (Her tekrarlanan eleman girin foove yapacak foo.getBarList()ki ve tekrarlayın barListtekrar)


map(): bir akış alın, her öğeye bir şeyler yapın, her işlemin tek bir sonucunu toplayın, başka bir akış elde edin. "Bir şey yap işlevi" tanımı örtüktür. Herhangi bir elemanın processment neden olursa null, nullson akışı oluşturmak için kullanılır. Dolayısıyla, sonuçtaki akıştaki elemanların sayısı giriş akışı sayısına eşit olacaktır.

flatmap(): bir öğe / akış akışı ve bir işlev (açık tanım) alın, işlevi her akışın her öğesine uygulayın ve elde edilen tüm ara akışı daha büyük bir akış ("düzleştirme") olarak toplayın. Herhangi bir elemanın işlenmesi sonucu ortaya çıkarsa null, "düzleştirme" nin son adımına boş akış sağlanır. Sonuçta elde edilen akıştaki eleman sayısı, eğer giriş birkaç akışsa, tüm girdilerdeki tüm katılımcı elemanların toplamıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.