簡體   English   中英

Java 8 Lambda分組同時使用X和Y.

[英]Java 8 Lambda groupingBy X and Y simultaneously

我正在尋找一個lambda來優化已檢索的數據。 我有一個原始結果集,如果用戶沒有更改我希望使用java的lambda按結果分組的日期。 而且我是java的新手lambdas。

我正在尋找的lambda與這個查詢相似。

select z, w, min(x), max(x), avg(x), min(y), max(y), avg(y) from table group by x, w;

所以我假設您有一個對象列表,並且您想要創建一個具有給定分組的地圖。 我對你的x,y,w,z有點困惑,所以我會用自己的字段。 但這是我將如何做到這一點:

interface Entry {
    String getGroup1();
    String getGroup2();
    int getIntData();
    double getDoubleData();
}

List<Entry> dataList;
Map<String, Map<String, IntSummaryStatistics>> groupedStats = 
    dataList.stream()
        .collect(Collectors.groupingBy(Entry::getGroup1,
            Collectors.groupingBy(Entry::getGroup2,
                Collectors.summarizingInt(Entry::getIntData))));

然后,如果你想獲得具有組A,B的項目的平均數據,那么你使用:

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

如果要同時匯總多個數據集,則會更復雜一些。 您需要編寫自己的包裝器類,可以累積多個統計信息。 這是一個包含Entry中兩個數據項的示例(我將它們設為int和double,以使其更有趣)。

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;
    }
}

然后可以使用此類創建自己的收集器:

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

現在你的地圖返回一個CompoundStats而不是一個IntSummaryStatistics:

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

另請注意,如果您創建了一個單獨的類來保存您的分組而不是使用我上面提到的兩步圖,那么這將更整潔。 如果需要,再次不是一個困難的修改

希望這對你自己的情況很有用。

我將在本練習中使用Tuple2類型 ,但是如果你想避免依賴,你也可以創建自己的元組類型。

我還假設您使用它來表示您的數據:

class A {
    final int w;
    final int x;
    final int y;
    final int z;

    A(int w, int x, int y, int z) {
        this.w = w;
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

你現在可以寫:

Map<Tuple2<Integer, Integer>, Tuple2<IntSummaryStatistics, IntSummaryStatistics>> map =
Stream.of(
    new A(1, 1, 1, 1),
    new A(1, 2, 3, 1),
    new A(9, 8, 6, 4),
    new A(9, 9, 7, 4),
    new A(2, 3, 4, 5),
    new A(2, 4, 4, 5),
    new A(2, 5, 5, 5))
.collect(Collectors.groupingBy(

    // This is your GROUP BY criteria
    a -> tuple(a.z, a.w),
    Collector.of(

        // When collecting, we'll aggregate data into two IntSummaryStatistics
        // for x and y
        () -> tuple(new IntSummaryStatistics(), new IntSummaryStatistics()),

        // The accumulator will simply take new t = (x, y) values
        (r, t) -> {
            r.v1.accept(t.x);
            r.v2.accept(t.y);
        },

        // The combiner will merge two partial aggregations,
        // in case this is executed in parallel
        (r1, r2) -> {
            r1.v1.combine(r2.v1);
            r1.v2.combine(r2.v2);

            return r1;
        }
    )
));

甚至更好(使用最新的jOOλAPI):

Map<Tuple2<Integer, Integer>, Tuple2<IntSummaryStatistics, IntSummaryStatistics>> map =

// Seq is like a Stream, but sequential only, and with more features
Seq.of(
    new A(1, 1, 1, 1),
    new A(1, 2, 3, 1),
    new A(9, 8, 6, 4),
    new A(9, 9, 7, 4),
    new A(2, 3, 4, 5),
    new A(2, 4, 4, 5),
    new A(2, 5, 5, 5))

// Seq.groupBy() is just short for Stream.collect(Collectors.groupingBy(...))
.groupBy(
    a -> tuple(a.z, a.w),

    // Because once you have tuples, why not add tuple-collectors?
    Tuple.collectors(
        Collectors.summarizingInt(a -> a.x),
        Collectors.summarizingInt(a -> a.y)
    )
);

地圖結構現在是:

(z, w) -> (all_aggregations_of(x), all_aggregations_of(y))

在上面的地圖上調用toString()將產生:

{
    (1, 1) = (IntSummaryStatistics{count=2, sum=3, min=1, average=1.500000, max=2}, 
              IntSummaryStatistics{count=2, sum=4, min=1, average=2.000000, max=3}), 
    (4, 9) = (IntSummaryStatistics{count=2, sum=17, min=8, average=8.500000, max=9}, 
              IntSummaryStatistics{count=2, sum=13, min=6, average=6.500000, max=7}), 
    (5, 2) = (IntSummaryStatistics{count=3, sum=12, min=3, average=4.000000, max=5}, 
              IntSummaryStatistics{count=3, sum=13, min=4, average=4.333333, max=5})
}

你現在收到了所有的統計數據。

邊注

當然,我不知道您的確切要求,但我懷疑您將很快需要在報表中進行更復雜的聚合,例如中位數,逆分布以及各種不錯的OLAP功能,這就是當您意識到SQL時對於這種任務來說,這只是一種更容易的語言。

另一方面,我們肯定會向jOOλ添加更多SQLesque功能 這個主題也激發了我寫一篇完整的博客文章,其中詳細介紹了所描述的方法

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM