Mapping
Often it is useful to map the
elements of one stream to another. For example, a stream that contains a
database of name, telephone, and e-mail address information might map only the
name and e-mail address portions to another stream. As another example, you
might want to apply some transformation to the elements in a stream. To do
this, you could map the transformed elements to a new stream. Because mapping
operations are quite common, the stream API provides built-in support for them.
The most general mapping method is map(
). It is shown here:
<R> Stream<R>
map(Function<? super T, ? extends R> mapFunc)
Here, R specifies the type of elements of the new stream; T is the type of elements of the
invoking stream; and mapFunc is an
instance of Function, which does the
mapping. The map function must be stateless and non-interfering. Since a new
stream is returned, map( ) is an
intermediate method.
Function is a functional interface declared in java.util.function. It is declared as shown here:
Function<T, R>
As it relates to map( ), T is the element type and R
is the result of the mapping. Function
has the abstract method shown here:
R apply(T val)
Here, val is a reference to the object being mapped. The mapped result is
returned. The following is a simple example of map( ). It provides a variation on the previous
example program. As before,
the program computes the product of the square roots of the values in an ArrayList. In this version, however,
the square roots of the elements are first mapped to a new stream. Then, reduce( ) is employed to compute the
product.
// Map one stream to another.
import java.util.*;
import java.util.stream.*;
class StreamDemo4 {
public static void main(String[] args) {
// A list of double values.
ArrayList<Double> myList = new ArrayList<>(
);
myList.add(7.0);
myList.add(18.0);
myList.add(10.0);
myList.add(24.0);
myList.add(17.0);
myList.add(5.0);
// Map the square root of the elements in
myList to a new stream.
Stream<Double> sqrtRootStrm =
myList.stream().map((a) -> Math.sqrt(a));
// Find the product of the square roots.
double productOfSqrRoots =
sqrtRootStrm.reduce(1.0, (a,b) -> a*b);
System.out.println("Product of square
roots is " + productOfSqrRoots);
}
}
The output is the same as
before. The difference between this version and the previous is simply that the
transformation (i.e., the computation of the square roots) occurs during
mapping, rather than during the reduction. Because of this, it is possible to
use the two-parameter form of reduce( )
to compute the product because it is no longer necessary to provide a separate
combiner function.
Here is an example that uses map( ) to create a new stream that
contains only selected fields from the original stream. In this case, the
original stream contains objects of type NamePhoneEmail,
which contains names, phone numbers, and e-mail addresses. The program then maps only the names and
phone numbers to a new stream of NamePhone
objects. The e-mail addresses are discarded.
//Use map() to create a new stream that contains
only
//selected aspects of the original stream.
import java.util.*;
import java.util.stream.*;
class NamePhoneEmail { String name;
String phonenum; String email;
NamePhoneEmail(String n, String p, String e) {
name = n;
phonenum = p; email = e;
}
}
class NamePhone { String name; String phonenum;
NamePhone(String n, String p) { name = n;
phonenum = p;
}
}
class StreamDemo5 {
public static void main(String[] args) {
// A list of names, phone numbers, and e-mail
addresses.
ArrayList<NamePhoneEmail> myList = new
ArrayList<>( );
myList.add(new
NamePhoneEmail("Larry", "555-5555",
"Larry@HerbSchildt.com"));
myList.add(new
NamePhoneEmail("James", "555-4444",
"James@HerbSchildt.com"));
myList.add(new NamePhoneEmail("Mary",
"555-3333", "Mary@HerbSchildt.com"));
System.out.println("Original values in
myList: "); myList.stream().forEach( (a) -> {
System.out.println(a.name + " " +
a.phonenum + " " + a.email); });
System.out.println();
// Map just the names and phone numbers to a
new stream.
Stream<NamePhone>
nameAndPhone = myList.stream().map(
(a) -> new NamePhone(a.name,a.phonenum) );
System.out.println("List of names and
phone numbers: "); nameAndPhone.forEach( (a) -> {
System.out.println(a.name + " " +
a.phonenum); });
}
}
The output, shown here,
verifies the mapping:
Original values in myList:
Larry 555-5555 Larry@HerbSchildt.com
James 555-4444 James@HerbSchildt.com
Mary 555-3333 Mary@HerbSchildt.com
List of names and phone numbers:
Larry 555-5555
James 555-4444
Mary 555-3333
Because you can pipeline more
than one intermediate operation together, you can easily create very powerful
actions. For example, the following statement uses filter( ) and then map( )
to produce a new stream that contains only the name and phone number of the
elements with the name "James":
Stream<NamePhone> nameAndPhone =
myList.stream().
filter((a) ->
a.name.equals("James")). map((a) -> new
NamePhone(a.name,a.phonenum));
This type of filter operation
is very common when creating database-style queries. As you gain experience
with the stream API, you will find that such chains of operations can be used
to create very sophisticated queries, merges, and selections on a data stream.
In addition to the version
just described, three other versions of map(
) are provided. They return a primitive stream, as shown here:
IntStream
mapToInt(ToIntFunction<? super T> mapFunc)
LongStream mapToLong(ToLongFunction<? super T> mapFunc) DoubleStream mapToDouble(ToDoubleFunction<? super T>
mapFunc)
Each mapFunc must implement the abstract method defined by the specified
interface, returning a value of the indicated type. For example, ToDoubleFunction specifies the applyAsDouble(T val ) method, which must return the value of its parameter as a double.
Here is an example that uses
a primitive stream. It first creates an ArrayList
of Double values. It then uses stream( ) followed by mapToInt( ) to create an IntStream that contains the ceiling of
each value.
// Map a Stream to an IntStream.
import java.util.*;
import java.util.stream.*;
class StreamDemo6 {
public static void main(String[] args) {
// A list of double values.
ArrayList<Double> myList = new
ArrayList<>( );
myList.add(1.1);
myList.add(3.6);
myList.add(9.2);
myList.add(4.7);
myList.add(12.1);
myList.add(5.0);
System.out.print("Original values in
myList: "); myList.stream().forEach( (a) -> {
System.out.print(a + " "); });
System.out.println();
// Map the ceiling of the elements in myList to
an IntStream.
IntStream cStrm = myList.stream().mapToInt((a)
-> (int) Math.ceil(a));
System.out.print("The ceilings of the
values in myList: "); cStrm.forEach( (a) -> {
System.out.print(a + " "); });
}
The output is shown here:
Original values in myList: 1.1 3.6 9.2 4.7 12.1
5.0
The ceilings of the values in myList: 2 4 10 5
13 5
The stream produced by mapToInt( ) contains the ceiling values
of the original elements in myList.
Before leaving the topic of
mapping, it is necessary to point out that the stream API also provides methods
that support flat maps. These are flatMap( ), flatMapToInt( ), flatMapToLong(
), and flatMapToDouble( ). The
flat map methods are designed to handle situations
in which each element in the original stream is mapped to more than one element
in the resulting stream.
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.