Streams: Downstream Collectors in Java

The groupingBy method yields a map whose values are lists. If you want to process those lists in some way, supply a downstream collector. For example, if you want sets instead of lists, you can use the Cottectors.toSet collector that you saw in the preceding section:

Map<String, Set<Locate>> countryToLocateSet = tocates.cottect(

groupingBy(Locate::getCountry, toSet()));

Several collectors are provided for reducing collected elements to numbers:

  • counting produces a count of the collected elements. For example,

Map<String, Long> countryToLocateCounts = tocates.cottect(

groupingBy(Locate::getCountry, counting()));

counts how many locales there are for each country.

  • summing(lnt | Long | Double) takes a function argument, applies the function to the downstream elements, and produces their sum. For example,

Map<String, Integer> stateToCityPoputation = cities.cottect(

groupingBy(City::getState, summingInt(City::getPoputation)));

computes the sum of populations per state in a stream of cities.

  • maxBy and minBy take a comparator and produce maximum and minimum of the downstream elements. For example,

Map<String, Optionat<City>> stateToLargestCity = cities.cottect(

groupingBy(City::getState,

maxBy(Comparator.comparing(City::getPoputation))));

produces the largest city per state.

The cottectingAndThen collector adds a final processing step behind a collector. For example, if you want to know how many distinct results there are, collect them into a set and then compute the size:

Map<Character, Integer> stringCountsByStartingLetter = strings.cottect(

groupingBy(s -> s.charAt(O),

mapping(String::length, toSet())));

Here, we group strings by their first character. Within each group, we produce the lengths and collect them in a set.

The mapping method also yields a nicer solution to a problem from the preceding section—gathering a set of all languages in a country.

Map<String, Set<String>> countryToLanguages = tocales.coltect(

groupingBy(Locate::getDisptayCountry,

mapping(Locate::getDisptayLanguage,

toSet())));

There is a ftatMapping method as well, for use with functions that return streams.

If the grouping or mapping function has return type int, tong, or double, you can collect elements into a summary statistics object, as discussed in Section 1.8, “Collecting Results,” on p. 25. For example,

Map<String, IntSummaryStatistics> stateToCityPopulationSummary = cities.collect(

groupingBy(City::getState,

summarizingInt(City::getPopulation)));

Then you can get the sum, count, average, minimum, and maximum of the function values from the summary statistics objects of each group.

The filtering collector applies a filter to each group, for example:

Map<String, Set<City>> largeCitiesByState

= cities.collect(

groupingBy(City::getState,

filtering(c -> c.getPopulation() > 500000,

toSet()))); // States without large cities have empty sets

Composing collectors is powerful, but it can lead to very convoluted expres­sions. The best use is with groupingBy or partitioningBy to process the “downstream” map values. Otherwise, simply apply methods such as map, reduce, count, max, or min directly on streams.

The example program in Listing 1.6 demonstrates downstream collectors.

Source: Horstmann Cay S. (2019), Core Java. Volume II – Advanced Features, Pearson; 11th edition.

Leave a Reply

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