It doesn't make sense to flatMap a Stream that's already flat, like the Stream<Integer> you've shown in your question.
However, if you had a Stream<List<Integer>> then it would make sense and you could do this:
Stream<List<Integer>> integerListStream = Stream.of(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5)
);
Stream<Integer> integerStream = integerListStream .flatMap(Collection::stream);
integerStream.forEach(System.out::println);
Which would print:
1
2
3
4
5
To do this pre-Java 8 you just need a loops:
List<List<Integer>> integerLists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5)
)
List<Integer> flattened = new ArrayList<>();
for (List<Integer> integerList : integerLists) {
flattened.addAll(integerList);
}
for (Integer i : flattened) {
System.out.println(i);
}
Answer from Nick Holt on Stack OverflowJava 8 Streams FlatMap method example - Stack Overflow
What's the difference between map() and flatMap ...
Is it possible to understand what flatMap does?
Alternative uses for `flatMap`
Videos
It doesn't make sense to flatMap a Stream that's already flat, like the Stream<Integer> you've shown in your question.
However, if you had a Stream<List<Integer>> then it would make sense and you could do this:
Stream<List<Integer>> integerListStream = Stream.of(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5)
);
Stream<Integer> integerStream = integerListStream .flatMap(Collection::stream);
integerStream.forEach(System.out::println);
Which would print:
1
2
3
4
5
To do this pre-Java 8 you just need a loops:
List<List<Integer>> integerLists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5)
)
List<Integer> flattened = new ArrayList<>();
for (List<Integer> integerList : integerLists) {
flattened.addAll(integerList);
}
for (Integer i : flattened) {
System.out.println(i);
}
Made up example
Imagine that you want to create the following sequence: 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 etc. (in other words: 1x1, 2x2, 3x3 etc.)
With flatMap it could look like:
IntStream sequence = IntStream.rangeClosed(1, 4)
.flatMap(i -> IntStream.iterate(i, identity()).limit(i));
sequence.forEach(System.out::println);
where:
IntStream.rangeClosed(1, 4)creates a stream ofintfrom 1 to 4, inclusiveIntStream.iterate(i, identity()).limit(i)creates a stream of length i ofinti - so applied toi = 4it creates a stream:4, 4, 4, 4flatMap"flattens" the stream and "concatenates" it to the original stream
With Java < 8 you would need two nested loops:
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 4; i++) {
for (int j = 0; j < i; j++) {
list.add(i);
}
}
Real world example
Let's say I have a List<TimeSeries> where each TimeSeries is essentially a Map<LocalDate, Double>. I want to get a list of all dates for which at least one of the time series has a value. flatMap to the rescue:
list.stream().parallel()
.flatMap(ts -> ts.dates().stream()) // for each TS, stream dates and flatmap
.distinct() // remove duplicates
.sorted() // sort ascending
.collect(toList());
Not only is it readable, but if you suddenly need to process 100k elements, simply adding parallel() will improve performance without you writing any concurrent code.
Both map and flatMap can be applied to a Stream<T> and they both return a Stream<R>. The difference is that the map operation produces one output value for each input value, whereas the flatMap operation produces an arbitrary number (zero or more) values for each input value.
This is reflected in the arguments to each operation.
The map operation takes a Function, which is called for each value in the input stream and produces one result value, which is sent to the output stream.
The flatMap operation takes a function that conceptually wants to consume one value and produce an arbitrary number of values. However, in Java, it's cumbersome for a method to return an arbitrary number of values, since methods can return only zero or one value. One could imagine an API where the mapper function for flatMap takes a value and returns an array or a List of values, which are then sent to the output. Given that this is the streams library, a particularly apt way to represent an arbitrary number of return values is for the mapper function itself to return a stream! The values from the stream returned by the mapper are drained from the stream and are passed to the output stream. The "clumps" of values returned by each call to the mapper function are not distinguished at all in the output stream, thus the output is said to have been "flattened."
Typical use is for the mapper function of flatMap to return Stream.empty() if it wants to send zero values, or something like Stream.of(a, b, c) if it wants to return several values. But of course any stream can be returned.
Stream.flatMap, as it can be guessed by its name, is the combination of a map and a flat operation. That means that you first apply a function to your elements, and then flatten it. Stream.map only applies a function to the stream without flattening the stream.
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 ].