0

What I read about flatMap:

To understand what flattening a stream consists in, consider a structure like [ [1,2,3],[4,5,6],[7,8,9] ] which has "two levels". Flattening this means transforming it in a "one level" structure : [ 1,2,3,4,5,6,7,8,9 ]

import java.util.List;
import java.util.stream.Stream;

public class FlatMapDemo {
    List<Country> countries;
    void demoFlatMap(){
        Stream<Stream<City>> streamStreamCity = countries
                .stream()
                .map(country -> country.cities.stream());
        streamStreamCity.flatMap(cityStream -> /*it's not City, it's Stream<City> !!!*/);
    }
}
class Country{
    List<City> cities;
}
class City {
    int population;
}

So I just wanna count all population from all countries but in cities which has > 1000 inhabitants. So I created Stream<Stream<City>> and hope flatMap flat for me Stream<Stream and give me City class to take something like: .flatMap(city -> city.population) to filter after: .filter(population > 1000) but it seems I was wrong. Please explain why flatMap doesn't do this? Isn't it the purpose of flattening?

Naman
  • 27,789
  • 26
  • 218
  • 353
J.J. Beam
  • 2,612
  • 2
  • 26
  • 55
  • There is no need to split flatMap as a separated operation, you can continue flatting this after the map() method. You flatted this to the Stream from Stream so now you can add filter like filter(county.getPopulation() > 1000) with a predicate which will filter Cities with > 1000 inhabitants and then use terminal operation collect(Collectors.counting()); at the end which will return the number of records which match the above predicate. – Martin Jul 24 '21 at 22:39
  • I split it only to expose the type of variable: `Stream>` and my question is: why `flatMap` doesn't reach `City` from `Stream>`? – J.J. Beam Jul 24 '21 at 22:45
  • To get a particular City you need to use findFirst(), findAny() or another one with which you can get a particular entity. You cannot reach the city directly from Stream> because its wrapped with the double stream. Firstly you are flatting to the Stream and from now you can apply filter and then use Collectors.counting() terminal operation to receive the desirable number of records that meet the population requirements. If you want to refer to the population you need to provide getters in your classes to make this field being visible in stream intermediate method calls. – Martin Jul 24 '21 at 23:02
  • How to `flatting to the Stream `? Can you provide a peace of code please? – J.J. Beam Jul 24 '21 at 23:14
  • 1
    so: `int sum = streamStreamCity.flatMap(cityStream -> cityStream) .filter(city -> city.population > 1000) .mapToInt(city -> city.population) .sum();` - right? – J.J. Beam Jul 25 '21 at 00:02
  • 2
    The `flatMap` operation flattens a stream-of-streams into a single stream. If I understand what you want correctly, you should be doing `int totalPopulation = countries.stream().flatMap(country -> country.cities.stream()).mapToInt(city -> city.population).filter(population -> population > 1000).sum()`. – Slaw Jul 25 '21 at 00:06
  • It seems I misunderstood the place to apply flatMap. It not applies to `Stream>` it produces `Stream>` and flatten it in place to `Stream>` - what `map` doesn't do – J.J. Beam Jul 25 '21 at 09:02
  • 1
    Does this answer your question? [What's the difference between map() and flatMap() methods in Java 8?](https://stackoverflow.com/questions/26684562/whats-the-difference-between-map-and-flatmap-methods-in-java-8) – Didier L Jul 25 '21 at 23:52
  • There is just copy/paste from documentation, I believe documentations and manuals answer everything but sometimes in a tough way. No it's not answered. The easy answer is: `flatMap` is not applies to `Stream>` it produces `Stream>` and flatten it immediately in place to `Stream>` - what map doesn't do. Perhaps it sounds stupid, but it helps to understand `flatMap` the really difficult topic – J.J. Beam Jul 26 '21 at 10:11
  • @J.J.Beam you are not supposed to end up with a `Stream>`. When it happens, it means that you used `map()` somewhere instead of `flatMap()`. This is the case here. Replace the former by the latter and your problem will be solved. – Didier L Jul 27 '21 at 07:39

1 Answers1

1

The goal of flatMap() is not to apply a transformation on the A items of a Stream<Stream<A>>. If you end up with such a type it most likely indicates you misused a map() operation somewhere.

The goal of flatMap() is instead to transform a Stream<A> into a Stream<B> when you have a function f that can produce a Stream<B> from individual A elements (simplifying a bit, it is a Function<A, Stream<B>>). I.e. it “flattens” the stream by avoiding to end up with nested streams.

You would use it like

getStreamOfA().flatMap(f) // returns Stream<B>

So in your example:

Stream<City> cityStream = countries
                .stream()
                .flatMap(country -> country.cities.stream());
Didier L
  • 18,905
  • 10
  • 61
  • 103