Using Stream.flatMap()

As we just saw, map() knows how to wrap a sequence of elements in a Stream.

This means that map() can produce streams such as Stream<String[]>, Stream<List<String>>, Stream<Set<String>>, or even Stream<Stream<R>>.

But the problem is that these kinds of streams cannot be manipulated successfully (or, as we expected) by stream operations such as sum(), distinct(), filter(), and so on.

For example, let's consider the following array of Melon:

Melon[][] melonsArray = {
{new Melon("Gac", 2000), new Melon("Hemi", 1600)},
{new Melon("Gac", 2000), new Melon("Apollo", 2000)},
{new Melon("Horned", 1700), new Melon("Hemi", 1600)}
};

We can take this array and wrap it in a stream via Arrays.stream(), as shown in the following snippet of code:

Stream<Melon[]> streamOfMelonsArray = Arrays.stream(melonsArray);
There are many other ways of obtaining a Stream of arrays. For example, if we have a string, s, then map(s -> s.split("")) will return a Stream<String[]>.

Now, we may think that obtaining the distinct Melon instances it is enough to call distinct(), as follows:

streamOfMelonsArray
.distinct()
.collect(Collectors.toList());

But this is not going to work because distinct() will not look for a distinct Melon; instead, it will look for a distinct array Melon[] because this is what we have in the stream.

Moreover, the result that was returned in this case is of the Stream<Melon[]> type, not of the Stream<Melon> type. The final result will collect Stream<Melon[]> in List<Melon[]>.

How we can fix this problem?

We may consider applying Arrays.stream() in order to convert the Melon[] into a Stream<Melon>:

streamOfMelonsArray
.map(Arrays::stream) // Stream<Stream<Melon>>
.distinct()
.collect(Collectors.toList());

Again, map() will not do what we might think it will do.

First, calling Arrays.stream() will return a Stream<Melon> from each of the given Melon[]. However, map() returns a Stream of elements, and so it will wrap the results of applying Arrays.stream() into a Stream. It will end up in a Stream<Stream<Melon>>.

So, this time, distinct() tries to detect distinct Stream<Melon> elements:

In order to fix this problem, we must rely on flatMap()The following diagram depicts how flatMap() works internally:

Unlike map(), this method returns a stream by flattening all the separated streams. So, all the arrays will end up in the same stream:

streamOfMelonsArray
.flatMap(Arrays::stream) // Stream<Melon>
.distinct()
.collect(Collectors.toList());

The output will contain distinct melons according to the Melon.equals() implementation:

Gac(2000g), Hemi(1600g), Apollo(2000g), Horned(1700g)

Now, let's try another problem, starting with a List<List<String>>, as follows:

List<List<String>> melonLists = Arrays.asList(
Arrays.asList("Gac", "Cantaloupe"),
Arrays.asList("Hemi", "Gac", "Apollo"),
Arrays.asList("Gac", "Hemi", "Cantaloupe"),
Arrays.asList("Apollo"),
Arrays.asList("Horned", "Hemi"),
Arrays.asList("Hemi"));

We try to obtain the distinct names of melons from this list. If wrapping an array into a stream can be done via Arrays.stream(), for a collection, we have Collection.stream(). Therefore, the first attempt may look as follows:

melonLists.stream()
.map(Collection::stream)
.distinct();

But based on the previous problem, we already know that this will not work because map() will return Stream<Stream<String>>.

The solution is provided by flatMap(), as follows:

List<String> distinctNames = melonLists.stream()
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());

The output is as follows:

Gac, Cantaloupe, Hemi, Apollo, Horned
Beside flatMap(), the Stream class also provides flavors for primitives such as flatMapToInt(), flatMapToLong(), and flatMapToDouble(). These methods return the int primitive specialization of Stream (IntStream), the long primitive specialization of Stream (LongStream), and the double primitive specialization of Stream (StreamDouble).
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset