Home » Java » Java: Group By then Map

Java: Group By then Map

Posted by: admin December 28, 2021 Leave a comment

Questions:

I have a stream of Events

public class Event {
    Location location;
    double turnout;
    //... other fields & getters
}

And a statistics class EventStatistics

public class EventStatistics {
    // Stats properties e.g. turnout standard deviation/median

    public EventStatistics(List<Event> events) {
        // Generate stats
    }
}

I need to group all the events by location & create a map of location and event statistics Map<Location, EventStatistics>

The group by is just:

Map<Location, List<Event>> byLocation = events.stream().collect(groupingBy(Event::getLocation));

I know there is an overloaded groupingBy(function, collector) collector. Can I use somehow this to generate my Map<Location, EventStatistics> in a single stream?

Answers:

All you need is collectingAndThen:

Map<Location, EventStatistics> result = 
    events.stream()
          .collect(Collectors.groupingBy(Event::getLocation,
                                         Collectors.collectingAndThen(
                                             Collectors.toList(), 
                                             EventStatistics::new)));

###

You can construct your own Collector using Collector.of(...), like this:

Map<Location, EventStatistics> collect = events.stream().collect(groupingBy(Event::getLocation,
        Collector.of(ArrayList::new,
                     List::add,
                     (left, right) -> { left.addAll(right); return left; },
                     EventStatistics::new)
));

###

If your EventStatistics were to be able to accept single Events instead of a full list, and a method to merge two statistics, as in

EventStatistics {
    public EventStatistics() {}
    public void addEvent(Event e);
    public EventStatistics merge(EventStatistics toMerge);
}

then you can do

groupingBy(Event::getLocation, Collector.of(EventStatistics::new, EventStatistics::accept, EventStatistics::merge));

Here, the argument-less constructor is the Supplier, the accept is the accumulator, and the merge is the combiner.