Java 8 Lambda groupingBy X and Y simultaneously

前端 未结 2 1621
心在旅途
心在旅途 2021-02-03 13:03

I\'m looking for a lambda to refine the data already retrieved. I have a raw resultset, if the user do not change the date I want use java\'s lambda to group by the results for

2条回答
  •  一生所求
    2021-02-03 13:16

    So I'm assuming you have a List of objects and you want to create a map with the given groupings. I am a bit confused by your x, y, w, z so I'll use my own fields. But Here's how I would do it:

    interface Entry {
        String getGroup1();
        String getGroup2();
        int getIntData();
        double getDoubleData();
    }
    
    List dataList;
    Map> groupedStats = 
        dataList.stream()
            .collect(Collectors.groupingBy(Entry::getGroup1,
                Collectors.groupingBy(Entry::getGroup2,
                    Collectors.summarizingInt(Entry::getIntData))));
    

    Then if you want to get, say, the average of data for items with groups A, B then you use:

    groupedStats.get("A").get("B").getAverage();
    

    If you want to summarise more than one set of data simultaneously then it gets a bit more complicated. You need to write your own wrapper class that can accumulate multiple statistics. Here's an example with both data items in Entry (I made them an int and a double to make it a bit more interesting).

    class CompoundStats {
        private final IntSummaryStatistics intDataStats = new IntSummaryStatistics();
        private final DoubleSummaryStatistics doubleDataStats = new DoubleSummaryStatistics();
    
        public void add(Entry entry) {
            intDataStats.accept(entry.getIntData());
            doubleDataStats.accept(entry.getDoubleData());
        }
    
        public CompoundStats combine(CompoundStats other) {
            intDataStats.combine(other.intDataStats);
            doubleDataStats.combine(other.doubleDataStats);
            return this;
        }
    }
    

    This class can then be used to create your own collector:

    Map> groupedStats = 
        dataList.stream()
            .collect(Collectors.groupingBy(Entry::getGroup1,
                Collectors.groupingBy(Entry::getGroup2,
                    Collector.of(CompoundStats::new, CompoundStats::add, CompoundStats::combine))));
    

    Now your maps return a CompoundStats instead of an IntSummaryStatistics:

    groupedStats.get("A").get("B").getDoubleStats().getAverage();
    

    Also note that this would be neater if you created a separate class to hold your groupings rather than using the two step map I've proposed above. Again not a difficult modification if required.

    Hopefully this is useful in your own case.

提交回复
热议问题