Mapping: Transforming Streams – Streams

Mapping: Transforming Streams

The map() operation has already been used in several examples (Example 16.3, p. 906, Example 16.4, p. 909, and Example 16.6, p. 920). Here we take a closer look at this essential intermediate operation for data processing using a stream. It maps one type of stream (Stream<T>) into another type of stream (Stream<R>); that is, each element of type T in the input stream is mapped to an element of type R in the output stream by the function (Function<T, R>) supplied to the map() method. It defines a one-to-one mapping. For example, if we are interested in the titles of CDs in the CD stream, we can use the map() operation to transform each CD in the stream to a String that represents the title of the CD by applying an appropriate function:

Click here to view code image

Stream<String> titles = CD.cdList
         .stream()                   // Input stream: Stream<CD>.
         .map(CD::title);            // Lambda expression: cd -> cd.title()

The following methods are defined in the Stream<T> interface, and analogous methods are also defined in the IntStream, LongStream, and DoubleStream interfaces:

Click here to view code image

<R> Stream<R> map(Function<? super T,? extends R> mapper)

Returns a stream consisting of the result of applying the given non-interfering, stateless function to the elements of this stream—that is, it creates a new stream (Stream<R>) from the results of applying the mapper function to the elements of this stream (Stream<T>).

This is an intermediate operation that does not change the stream size, but it can change the stream element type and does not guarantee to preserve the encounter order of the input stream.

Click here to view code image

// Converting Stream<T> to a Numeric Stream
IntStream    mapToInt(ToIntFunction<? super T> mapper)
LongStream   mapToLong(ToLongFunction<? super T> mapper)
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)

Return the numeric stream consisting of the results of applying the given non-interfering, stateless function to the elements of this stream—that is, they create a stream of numeric values that are the results of applying the mapper function to the elements of this stream (Stream<T>).

These operations are all intermediate operations that transform an object stream to a numeric stream. The stream size is not affected, but there is no guarantee that the encounter order of the input stream is preserved.

In Figure 16.7, the query creates a list with CD titles released in 2018. The stream pipeline uses a filter() operation first to select CDs released in 2018, and the map() operation maps a CD to its title (String). The input stream is transformed by the map() operation from Stream<CD> to Stream<String>. The execution of this stream pipeline shows the resulting list (List<String>) containing three CD titles.

Figure 16.7 Mapping

The query below illustrates transforming an object stream to a numeric stream. When executed, the stream pipeline prints the years in which the CDs were released. Note the transformation of the initial stream, Stream<CD>. The map() operation first transforms it to a Stream<Year> and the distinct() operation selects the unique years. The mapToInt() operation transforms the stream from Stream<Year> to IntStream—that is, a stream of ints whose values are then printed.

Click here to view code image

CD.cdList.stream()                                   // Stream<CD>
         .map(CD::year)                              // Stream<Year>
         .distinct()                                 // Stream<Year>
         .mapToInt(Year::getValue)                   // IntStream
         .forEach(System.out::println);              // 2017
                                                     // 2018

In the example below, the range() method generates an int stream for values in the half-open interval specified by its arguments. The values are generated in increasing order, starting with the lower bound of the interval. In order to generate them in decreasing order, the map() operation can be used to reverse the values. In this case, the input stream and output stream of the map() operation are both IntStreams.

Click here to view code image

int from = 0, to = 5;
IntStream.range(from, to)                   // [0, 5)
         .map(i -> to + from – 1 – i)       // Reverse the stream values
         .forEach(System.out::print);       // 43210

The stream pipeline below determines the number of times the dice value is 6. The generate() method generates a value between 1 and 6, and the limit() operation limits the max size of the stream. The map() operation returns the value 1 if the dice value is 6; otherwise, it returns 0. In other words, the value of the dice throw is mapped either to 1 or 0, depending on the dice value. The terminal operation sum() sums the values in the streams, which in this case are either 1 or 0, thus returning the correct number of times the dice value was 6.

Click here to view code image

long sixes = IntStream
   .generate(() -> (int) (6.0 * Math.random()) + 1) // [1, 6]
   .limit(2000)                                     // Number of throws.
   .map(i -> i == 6 ? 1 : 0)             // Dice value mapped to 1 or 0.
   .sum();

Leave a Reply

Your email address will not be published. Required fields are marked *