An intermediate operation returns a Stream object that emits (or not) the same or modified value(s) of the same or different type than the stream source. In this installment, we will discuss intermediate operations that create a new stream of values of the same or another type.
The flatMap() operation (method) of the Stream interface is the central stream operator, as it is able to take an input value and produce a stream of values of a different type. Its interface looks like map(), covered in the previous post, but map() transforms a single value to another value, while flatMap() transforms a single value of the source type into, typically, many (stream of) values of another type. It is an intermediate operation as it returns a Stream object:
— Stream<R> flatMap(Function<T, R> mapper). Applies the provided function to each element of type T of the input stream and produces and produces a Stream<R> object that emits elements of type R.
— IntStream flatMapToInt(Function<T, IntStream> mapper). Applies the provided function to each element of type T of the input stream and produces an IntStream object that emits elements of type int.
— LongStream flatMapToLong(Function<T, LongStream> mapper). Applies the provided function to each element of type T of the input stream and produces a new element value of type long.
— DoubleStream flatMapToIntDouble(Function<T, DoubleStream> mapper). Applies the provided function to each element of type T of the input stream and produces a new element value of type double.
As you can see, flatMapTo() operations are just specialized flatMap() operations that can convert a stream of objects of any type to a numeric stream. Please, notice that transformation (mapping) to a numeric type could be done using a generic map() too. But the output stream of values of type R (produced by Stream<R> object) will not have some of the operators specific to the numeric streams, like sum() or average(), for example.
The following the code that demonstrates flatMap() usage:
List<String> list = List.of("one", "two", "three");
list.stream()
.flatMap(s -> Arrays.stream(s.split("")))
.map(s -> s + " ")
.forEach(System.out::print);
//prints: o n e t w o t h r e e
list.stream()
.flatMapToInt(s -> IntStream.range(0, s.length()))
.forEach(System.out::print); //prints: 01201201234
list.stream()
.flatMapToLong(s -> LongStream.range(0, s.length()))
.forEach(System.out::print); //prints: 01201201234
list.stream()
.flatMapToDouble(s -> {
double[] arr = new double[s.length()];
for(int i = 0; i < s.length(); i++ ){
arr[i] = Math.round(i * 0.1 * 10) / 10.;
}
return DoubleStream.of(arr);
})
.mapToObj(d -> d + " ")
.forEach(System.out::print);
//prints: 0.0 0.1 0.2 0.0 0.1 0.2 0.0 0.1 0.2 0.3 0.4
These examples are very simple–just conversion with minimal processing. But in real life, each flatMap() operation typically accepts a more complex function that does something more useful.
In the next post, we will start discussing terminal operations and its first group of three operations:
— long count();
— void forEach(Consumer<T> action);
— void forEachOrdered(Consumer <T> action).
See other posts on Java 8 streams and posts on other topics.
You can also use navigation pages for Java stream related blogs:
— Java 8 streams blog titles
— Create stream
— Stream operations
— Stream operation collect()
The source code of all the code examples is here in GitHub.
Send your comments using the link Contact or in response to my newsletter.
If you do not receive the newsletter, subscribe via link Subscribe under Contact.