Flattening Streams – Streams
Flattening Streams
The flatMap() operation first maps each element in the input stream to a mapped stream, and then flattens the mapped streams to a single stream—that is, the elements of each mapped stream are incorporated into a single stream when the pipeline is executed. In other words, each element in the input stream may be mapped to many elements in the output stream. The flatMap() operation thus defines a one-to-many mapping that flattens a multilevel stream by one level.
The following method is defined in the Stream<T> interface, and an analogous method is also defined in the IntStream, LongStream, and DoubleStream interfaces:
<R> Stream<R> flatMap(
Function<? super T,? extends Stream<? extends R>> mapper)
The mapper function maps each element of type T in this stream to a mapped stream (Stream<R>). The method returns an output stream (Stream<R>) which is the result of replacing each element of type T in this stream with the elements of type R from its mapped stream.
If the result of the mapper function is null, the empty stream is used as the mapped stream.
This is an intermediate operation that changes the stream size and the element type of the stream, and does not guarantee to preserve the encounter order of the input stream.
The methods below are defined only in the Stream<T> interface. No counterparts exist in the IntStream, LongStream, or DoubleStream interfaces:
IntStream flatMapToInt(Function<? super T,? extends IntStream> mapper)
LongStream flatMapToLong(Function<? super T,? extends LongStream> mapper)
DoubleStream flatMapToDouble(
Function<? super T,? extends DoubleStream> mapper)
The mapper function maps each element of type T in this stream to a mapped numeric stream (NumTypeStream). The method returns an output stream (NumType-Stream), which is the result of replacing each element of type T in this stream with the values from its mapped numeric stream. The designation NumType stands for Int, Long, or Double.
To motivate using the flatMap() operation, we look at how to express the query for creating a list of unique CDs from two given lists of CDs. Figure 16.8 shows an attempt to express this query by creating a stream of lists of CDs, Stream<List<CD>>, and selecting the unique CDs using the distinct() method. This attempt fails miserably as the distinct() method distinguishes between elements that are lists of CDs, and not individual CDs. Figure 16.8 shows the execution of the stream pipeline resulting in a list of lists of CDs, List<List<CD>>.
Figure 16.8 Incorrect Solution to the Query
The next attempt to express the query uses the map() operation as shown in Figure 16.9. The idea is to map each list of CDs (List<CD>) to a stream of CDs (Stream<CD>), and select the unique CDs with the distinct() operation. The mapper function of the map() operation maps each list of CDs to a mapped stream that is a stream of CDs, Stream<CD>. The resulting stream from the map() operation is a stream of streams of CDs, Stream<Stream<CD>>. The distinct() method distinguishes between elements that are mapped streams of CDs. Figure 16.9 shows the execution of the stream pipeline resulting in a list of mapped streams of CDs, List<Stream<CD>>.
Figure 16.9 Mapping a Stream of Streams
The flatMap() operation provides the solution, as it flattens the contents of the mapped streams into a single stream so that the distinct() operation can select the unique CDs individually. The stream pipeline using the flatMap() operation and its execution are shown in Figure 16.10. The mapper function of the flatMap() operation maps each list of CDs to a mapped stream that is a stream of CDs, Stream<CD>. The contents of the mapped stream are flattened into the output stream. The resulting stream from the flatMap() operation is a stream of CDs, Stream<CD>. Note how each list in the initial stream results in a flattened stream whose elements are processed by the pipeline. The result list of CDs contains the unique CDs from the two lists.
Figure 16.10 Flattening Streams
The code below flattens a two-dimensional array to a one-dimensional array. The Arrays.stream() method call at (1) creates an object stream, Stream<int[]>, whose elements are arrays that are rows in the two-dimensional array. The mapper of the flatMapToInt() operation maps each row in the Stream<int[]> to a stream of ints (IntStream) by applying the Array.stream() method at (2) to each row. This would result in a stream of mapped streams of ints (Stream<IntStream>>), but it is flattened by the flatMapToInt() operation to a final stream of ints (IntStream). The terminal operation toArray() creates an appropriate array in which the int values of the final stream are stored (p. 971).
int[][] twoDimArray = { {2017, 2018}, {1948, 1949} };
int[] intArray = Arrays
.stream(twoDimArray) // (1) Stream<int[]>
.flatMapToInt(row -> Arrays.stream(row)) // (2) mapper: int[] -> IntStream,
// flattens Stream<IntStream> to IntStream.
.toArray(); // [2017, 2018, 1948, 1949]
Leave a Reply