In Java 8, what's the difference between Stream.map
and Stream.flatMap
methods?
相关问题
- Delete Messages from a Topic in Apache Kafka
- Jackson Deserialization not calling deserialize on
- How to maintain order of key-value in DataFrame sa
- StackExchange API - Deserialize Date in JSON Respo
- Difference between Types.INTEGER and Types.NULL in
The function you pass to
stream.map
has to return one object. That means each object in the input stream results in exactly one object in the output stream.The function you pass to
stream.flatMap
returns a stream for each object. That means the function can return any number of objects for each input object (including none). The resulting streams are then concatenated to one output stream.I would like to give 2 examples to get a more practical point of view:
1st example making usage of map:
Nothing special in the first example, a
Function
is applied to return theString
in uppercase.Second example making usage of
flatMap
:In the second example, a Stream of List is passed. It is NOT a Stream of Integer!
If a transformation Function has to be used (through map), then first the Stream has to be flattened to something else (a Stream of Integer).
If flatMap is removed then the following error is returned: The operator + is undefined for the argument type(s) List, int.
It is NOT possible to apply + 1 on a List of Integers!
Please go through the post fully to get a clear idea, map vs flatMap: To return a length of each word from a list, we would do something like below..
For example:-
Consider a list [“STACK”, ”OOOVVVER”] and we are trying to return a list like [“STACKOVER”](returning only unique letters from that list) Initially, we would do something like below to return a list [“STACKOVER”] from [“STACK”, ”OOOVVVER”]
Here the issue is, Lambda passed to the map method returns a String array for each word, So the stream returned by the map method is actually of type Stream, But what we need is Stream to represent a stream of characters, below image illustrates the problem.
Figure A:
You might think that, We can resolve this problem using flatmap,
OK, let us see how to solve this by using map and Arrays.stream First of all you gonna need a stream of characters instead of a stream of arrays. There is a method called Arrays.stream() that would take an array and produces a stream, for example:
The above still does not work, because we now end up with a list of streams (more precisely, Stream>), Instead, we must first convert each word into an array of individual letters and then make each array into a separate stream
By using flatMap we should be able to fix this problem as below:
flatMap would perform mapping each array not with stream but with the contents of that stream. All of the individual streams that would get generated while using map(Arrays::stream) get merged into a single stream. Figure B illustrates the effect of using the flatMap method. Compare it with what map does in figure A. Figure B
The flatMap method lets you replace each value of a stream with another stream and then joins all the generated streams into a single stream.
I have a feeling that most answers here overcomplicate the simple problem. If you already understand how the
map
works that should be fairly easy to grasp.There are cases where we can end up with unwanted nested structures when using
map()
, theflatMap()
method is designed to overcome this by avoiding wrapping.Examples:
1
We can avoid having nested lists by using
flatMap
:2
where:
This is very confusing for beginners. The basic difference is
map
emits one item for each entry in the list andflatMap
is basically amap
+flatten
operation. To be more clear, use flatMap when you require more than one value, eg when you are expecting a loop to return arrays, flatMap will be really helpful in this case.I have written a blog about this, you can check it out here.
map() and flatMap()
map()
Just takes a Function a lambda param where T is element and R the return element built using T. At the end we'll have a Stream with objects of Type R. A simple example can be:
It simply takes elements 1 to 5 of Type
Integer
, uses each element to build a new element from typeString
with value"prefix_"+integer_value
and prints it out.flatMap()
It is useful to know that flapMap() takes a function
F<T, R>
whereT is a type from which a Stream can be built from/with. It can be a List (T.stream()), an array (Arrays.stream(someArray)), etc.. anything that from which a Stream can be with/or form. in the example below each dev has many languages, so dev. Languages is a List and will use a lambda parameter.
R is the resulting Stream that will be built using T. Knowing that we have many instances of T, we will naturally have many Streams from R. All these Streams from Type R will now be combined into one single 'flat' Stream from Type R.
Example
The examples of Bachiri Taoufiq see its answer here are simple and easy to understanding. Just for clarity, let just say we have a team of developers:
, with each developer knowing many languages:
Applying Stream.map() on dev_team to get the languages of each dev:
will give you this structure:
which is basically a
List<List<Languages>> /Object[Languages[]]
. Not so very pretty, nor Java8-like!!with
Stream.flatMap()
you can 'flatten' things out as it takes the above structureand turns it into
{lang_a, lang_b, lang_c, lang_d, lang_e, lang_f}
, which can basically used asList<Languages>/Language[]/ect
...so the end your code would make more sense like this:
or simply:
When to use map() and use flatMap():
Use
map()
when each element of type T from your stream is supposed to be mapped/transformed to a single element of type R. The result is a mapping of type (1 start element -> 1 end element) and new stream of elements of type R is returned.Use
flatMap()
when each element of type T from your stream is supposed to mapped/transformed to a Collections of elements of type R. The result is a mapping of type (1 start element -> n end elements). These Collections are then merged (or flattened) to a new stream of elements of type R. This is useful for example to represent nested loops.Pre Java 8:
Post Java 8