Home Page
            >
            Collections
            >
            Aggregate Operations
        
        
        Answers to Questions and Exercises: Aggregate Operations
        
Questions
  - Q: A sequence of aggregate operations is known as a ___ .
  
 A: Pipeline
 
 
- Q: Each pipeline contains zero or more ___ operations.
  
 A: Intermediate
 
 
- Q: Each pipeline ends with a ___ operation.
 
 A: Terminal
 
 
- Q: What kind of operation produces another stream as its output?
  
 A: Intermediate
 
 
- Q: Describe one way in which the forEachaggregate operation differs from the enhancedforstatement
 or iterators.
 A: TheforEachaggregate operation lets the system decide "how" the iteration takes place.
 Using aggregate operations lets
 you focus on "what" instead of "how."
 
 
- Q: True or False: A stream is similar to a collection in that it
 is a data structure that stores elements.
 
 A: False. Unlike a collection, a stream is not a data structure. It instead carries
values from a source through a pipeline.
 
 
- Q: Identify the intermediate and terminal operations in this code:
double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();
 A: Intermediate:filter,mapToInt
 Terminal:average
 The terminal operationaveragereturns anOptionalDouble. ThegetAsDoublemethod is then invoked on that returned object.  It is always a good 
idea to consult the 
API Specification for information about whether an operation is intermediate 
or terminal.
 
 
- Q: The code
p -> p.getGender() == Person.Sex.MALE
is an example of what?
 A: A lambda expression.
 
 
- 
Q: The code
Person::getAge
is an example of what?
 A: A method reference.
 
 
- 
Q: Terminal operations that combine the contents of a stream and return one value
are known as what?
 A: Reduction operations.
 
 
- 
Q: Name one important difference between the Stream.reducemethod
and theStream.collectmethod.
 A:Stream.reducealways creates a new value when it processes an element.Stream.collectmodifies (or mutates) the existing value.
 
 
- 
Q: If you wanted to process a stream of names, extract the male names, and
store them in a new List, wouldStream.reduceorStream.collectbe the
most appropriate operation to use?
 A: The collect operation is most appropriate for collecting into aList.
 
 Example:
List<String> namesOfMaleMembersCollect = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .map(p -> p.getName())
    .collect(Collectors.toList());
 
 
- 
Q: True or False: Aggregate operations make it possible to
implement parallelism with non-thread-safe collections.
 A: True, provided that you do not modify (mutate) the underlying collection 
while you are operating on it.
 
 
- 
Q: Streams are always serial unless otherwise specified. How
 do you request that a stream be processed in parallel?
 A: Obtain the parallel stream by invokingparallelStream()instead ofstream().
 
 
Exercises
 - 
Exercise: Write the following enhanced forstatement as a
pipeline with lambda expressions. Hint: Use thefilterintermediate operation and theforEachterminal
operation.
 
for (Person p : roster) {
    if (p.getGender() == Person.Sex.MALE) {
        System.out.println(p.getName());
    }
}
 Answer:
roster
    .stream()
    .filter(e -> e.getGender() == Person.Sex.MALE)
    .forEach(e -> System.out.println(e.getName());
- Convert the following code into a new implementation that
uses lambda expressions and aggregate operations instead of nested
forloops. Hint: Make a pipeline that invokes thefilter,sorted, andcollectoperations, in that order.
List<Album> favs = new ArrayList<>();
for (Album a : albums) {
    boolean hasFavorite = false;
    for (Track t : a.tracks) {
        if (t.rating >= 4) {
            hasFavorite = true;
            break;
        }
    }
    if (hasFavorite)
        favs.add(a);
}
Collections.sort(favs, new Comparator<Album>() {
                           public int compare(Album a1, Album a2) {
                               return a1.name.compareTo(a2.name);
                           }});
 Answer:
List<Album> sortedFavs =
  albums.stream()
        .filter(a -> a.tracks.anyMatch(t -> (t.rating >= 4)))
        .sorted(Comparator.comparing(a -> a.name))
        .collect(Collectors.toList());
 Here we have used the stream operations to simplify each of the three major steps -- identification of whether any track in an album has a rating of at least 4 (anyMatch), the sorting, and the collection of albums matching our criteria into aList. TheComparator.comparing()method takes a function that extracts aComparablesort key, and returns aComparatorthat compares on that key.