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.