简体   繁体   中英

Java Streams: Grouping, Summing AND Counting

I'm new to streams but very intrigued with the possibilities.

I'm trying to write a stream that does grouping, counting and summing at the same time. The data involved is actually quite simple but writing the streaming statement I need is proving challenging and I'm not seeing anything really helpful in Google searches.

First, let me describe my data, then I'll show you how I've solved two-thirds of the problem. Perhaps you can tell me how to fit in the missing piece.

The data is ticket sales from a company that sells concert tickets. Each sale consists of an agency code, an order number, order date and the number of tickets sold. Therefore, it looks like this:

AgencyCode  OrderNumber OrderDate  TicketsSold
----------  ----------- ---------  -----------
TW          111111  2016-03-01          4
TW          111112  2016-03-01          2
CP          201000  2016-03-01          3
TW          111113  2016-03-01          8
CP          201001  2016-03-02          2
EL          300001  2016-03-01          4
AS          400000  2016-03-02          2

What I'm trying to get out of this data is a summary showing the total number of orders for each agency code and the total number of tickets sold for that same agency code. Therefore, the values I want to get for this particular set of data is:

AgencyCode  Orders   TicketsSold
TW               3            14
CP               2             5
EL               1             4
AS               1             2

I've got the grouping working and also the number of tickets sold. It's just the counting of the orders that I'm trying to get.

Here's how I got the tickets sold by agency:

 Map<String, Integer> salesByAgency
     = ticketOrders.stream()
         .collect(Collectors.groupingBy(TicketSale::getAgencyCode,
                 Collectors.summingInt(TicketSale::getTicketsSold)));

TicketSale is the class that holds a single ticket order. My collection, ticketOrders , is a LinkedHashSet holding a bunch of TicketSale records.

How do I adjust what I have to get the number of orders for each agency code?

You can use

Map<String, Integer> orders = ticketOrders
  .stream()
  .collect(Collectors.groupingBy(TicketSale::getAgencyCode,
                                 Collectors.summingInt(x -> 1)));

or

Map<String, Long> orders = ticketOrders
  .stream()
  .collect(Collectors.groupingBy(TicketSale::getAgencyCode,
                                 Collectors.counting()));

to get the number of orders by agency.

If you want to group count and orders simultaneously you have to define your own collector, eg

Map<String, int[]> grouped = ticketOrders
  .stream()
  .collect(Collectors.groupingBy(TicketSale::getAgencyCode,
                                 Collector.of(
                                     () -> new int[2],
                                     (a, t) -> { a[0] += 1; a[1] += t.getTicketsSold(); },
                                     (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; })));

However, this kind of lambdaism might be hard to understand. :-)

[edit] The collector is composed of three parts in this case. The first line is the supplier that creates a new result container, in this case an array with two elements: one for the count, one for the sum. The second line is the accumulator ; it adds data to an existing result container. The third line is the combiner that is used to merge two result containers into one.

For a detailed explanation you might, as always, consult the Java API documentation .

The simplest solution would be to use Collectors.summarizingInt() :

Map<String, IntSummaryStatistics> salesByAgency
     = ticketOrders.stream()
         .collect(Collectors.groupingBy(TicketSale::getAgencyCode,
                 Collectors.summarizingInt(TicketSale::getTicketsSold)));

The IntSummaryStatistics class maintains count, sum, min and max values. So after this you can get the sum for some group:

long sum = salesByAgency.get(agencyCode).getSum();

But you can also get the count:

long count = salesByAgency.get(agencyCode).getCount();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM