Wat is het verschil tussen concatMap en flatMap in RxJava

Het lijkt erop dat deze 2 functies vrij gelijkaardig zijn. Ze hebben dezelfde handtekening (accepteren rx.functions.Func1<? super T, ? extends Observable<? extends R>> func), en hun marmeren diagrammen zien er precies hetzelfde uit. Ik kan de foto’s hier niet plakken, maar hier is er een voor concatMap, en hier is er een voor flatMap. Er lijkt een subtiel verschil te zijn in de beschrijving van de resulterende Observable, waar een gemaakt door concatMapitems bevat die het resultaat zijn van het aaneenschakelen van resulterende Observables, en die geproduceerd door flatMapbevat items die het resultaat zijn van het eerst samenvoegen van de resulterende Observables en het uitzenden van het resultaat van die fusie.

Deze subtiliteit is mij echter totaal onduidelijk. Kan iemand een betere verklaring geven voor dit verschil, en idealiter enkele voorbeelden geven die dit verschil illustreren.


Antwoord 1, autoriteit 100%

Zoals je schreef, lijken de twee functies erg op elkaar en het subtiele verschil is hoe de uitvoer wordt gemaakt (nadat de mapping-functie is toegepast).

Flat map gebruikt samenvoegoperatorterwijl concatMap concat-operator.

Zoals je ziet is de concatMap-uitvoerreeks geordend – alle items die door de eerste Observable worden uitgezonden, worden uitgezonden voordat een van de items die door de tweede Observable worden uitgezonden,
terwijl de uitvoervolgorde van flatMap wordt samengevoegd – de items die door de samengevoegde Observable worden uitgezonden, kunnen in elke volgorde verschijnen, ongeacht van welke bron Observable ze afkomstig zijn.


Antwoord 2, autoriteit 40%

Hoewel de antwoorden hier goed zijn, was het niet gemakkelijk om het verschil te zien zonder een voorbeeld. Daarom heb ik hiervoor een eenvoudig voorbeeld gemaakt:

@Test
public void flatMapVsConcatMap() throws Exception {
    System.out.println("******** Using flatMap() *********");
    Observable.range(1, 15)
            .flatMap(item -> Observable.just(item).delay(1, TimeUnit.MILLISECONDS))
            .subscribe(x -> System.out.print(x + " "));
    Thread.sleep(100);
    System.out.println("\n******** Using concatMap() *********");
    Observable.range(1, 15)
            .concatMap(item -> Observable.just(item).delay(1, TimeUnit.MILLISECONDS))
            .subscribe(x -> System.out.print(x + " "));
    Thread.sleep(100);
}

******** FlatMap() gebruiken *********

1 2 3 4 5 6 7 9 8 11 13 15 10 12 14

******** ConcatMap() gebruiken *********

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Zoals uit de uitvoer blijkt, zijn de resultaten voor flatMapongeordend, terwijl dat voor concatMapwel het geval is.


Antwoord 3, autoriteit 28%

Een heel belangrijk verschil: de concatMapwacht tot de huidige uitgezonden waarneembare gegevens zijn voltooid en flatMapniet. flatMapprobeert er zoveel mogelijk te starten. Simpel gezegd – je kunt iets oneindigs niet aaneenschakelen. Zorg er wel voor dat de waarneembare objecten die u uitzendt in concatMapkunnen worden voltooid, anders loopt de hele stroom vast en wacht totdat de huidige waarneembare gegevens zijn voltooid om de volgende samen te voegen.


Antwoord 4, autoriteit 16%

Ik vind het voorbeeld in het meest geüpdatete antwoord niet erg duidelijk, dus ik plaats er een die me hielp het verschil tussen flatMap en concatMap te begrijpen.

FlatMap haalt emissies uit de waarneembare bron, maakt vervolgens een nieuwe waarneembare en samenvoegingdeze in de originele keten, terwijl concatMap concatdeze in de originele keten.

Belangrijk verschil is dat concatMap() zal samenvoegen
elk in kaart gebracht Observable opeenvolgend en vuur het een voor een. Het zal alleen verhuizen naar de
next Waarneembaar wanneer de huidige onComplete() aanroept.

Hier is flatMapvoorbeeld:

private void flatMapVsConcatMap() throws InterruptedException {
    Observable.just(5, 2, 4, 1)
            .flatMap(
                    second ->
                            Observable.just("Emit delayed with " + second + " second")
                                    .delay(second, TimeUnit.SECONDS)
            )
            .subscribe(
                    System.out::println,
                    Throwable::printStackTrace
            );
    Thread.sleep(15_000);
}

Zal resulteren in:

Uitzending vertraagd met 1 seconde
Uitzending vertraagd met 2 seconden
Uitzending vertraagd met 4 seconden
Uitzenden vertraagd met 5 seconden

Hier is concatMapvoorbeeld:

private void flatMapVsConcatMap() throws InterruptedException {
    Observable.just(5, 2, 4, 1)
            .concatMap(
                    second ->
                            Observable.just("Emit delayed with " + second + " second")
                                    .delay(second, TimeUnit.SECONDS)
            )
            .subscribe(
                    System.out::println,
                    Throwable::printStackTrace
            );
    Thread.sleep(15_000);
}

Zal resulteren in:

Uitzending vertraagd met 5 seconden
Uitzending vertraagd met 2 seconden
Uitzending vertraagd met 4 seconden
Uitzenden vertraagd met 1 seconde

Opmerking om Thread.sleep()te gebruiken omdat delay()standaard werkt op de rekenplanner


Antwoord 5

Allereerst is flatMap hetzelfde als mergeMap in Rxjs. Dat is dus weer een verwarring minder.
Er zijn dus twee waarneembare dingen..

1) o1: Een eenvoudige lijst met items van([‘Kitty’,’Donald’,’Batman’])

2) process_o1(): process_o1() is een functie die als één parameter ‘item’ neemt en er iets mee doet en een Observable retourneert die bij voltooiing ‘klaar met [item]’ uitstraalt.

o1.pipe(mergeMap(item => process_o1(item))).subscribe(data => {
console.log(data);
});

Hier gaan we zien: –
klaar met Kity.

klaar met Donald.

klaar met Batman.

zonder enige garantie dat Kitty voor Donald komt en Donald voor Batman.
Dit komt omdat, zodra het buitenste waarneembare een item uitzendt, het binnenste waarneembare is
geabonneerd.

===
Maar in het geval van concatMap:-

o1.pipe(concatMap(item => process_o1(item))).subscribe(data => {
console.log(data);
});

We hebben de garantie van de onderstaande volgorde:-

klaar met Kity.

klaar met Donald.

klaar met Batman.

Omdat, met concatMap-operator, de inner Observable niet wordt geabonneerd voordat de vorige inner Observable terugkeert.

Het buitenste waarneembare is vrij om gewoon door te gaan en al zijn waarden uit te zenden, maar de concatMap zal ervoor zorgen dat het elk van die waarden één voor één behandelt en de volgorde handhaaft.
Vandaar de naam concatMap.

Kortom, als je de volgorde van dingen graag wilt behouden, moet je concatMap gebruiken.
Maar als je niet om orde geeft, kun je doorgaan met mergeMap, dat zich in één keer abonneert op alle innerlijke Observables en waarden blijft uitzenden als en wanneer ze terugkeren.


Antwoord 6

flatMap versus concatMap

flatMap– samenvoegen – als een nieuw item wordt verzonden, heeft dit prioriteit

concatMap– samenvoegen – toevoegen aan het einde – volledige reeks uitzenden en pas daarna (vorige was voltooid) kan de volgende reeks worden uitgezonden

[kaart vs flatMap]


Antwoord 7

Anderen hebben het antwoord al aangegeven, maar als het niet al te duidelijk is, bestaat het risico dat er een ongewenst parallellisme ontstaat met flatMap. Als dit ongewenst is, kunt u concatMap gebruiken, of de overbelaste flatMap(Function<? super T,? extends Publisher<? extends V>> mapper, int concurrency)

Other episodes