簡體   English   中英

將多個 `Collectors::groupBy` 函數與 Java Streams 結合使用

[英]Combine multiple `Collectors::groupBy` functions with Java Streams

我在正確組合多個Collectors::groupingBy函數然后一次將它們全部應用於給定輸入時遇到問題。

假設我有一些實現以下接口的類:

interface Something {
    String get1();
    String get2();
    String get3();
    String get4();
}

現在我可以從這個接口獲得一些方法組合的列表,即這些列表可以是: [Something::get1, Something::get3] , [Something::get2, Something::get1, Something::get3] .

現在,有了這樣的方法列表和一些東西的列表,我想通過 getter 對這些東西進行分組。

我的意思是,例如,對於列表[Something::get1, Something::get3]和一個列表[Something1, Something2, ...]我想通過先分組出頭的列表get1通過,然后get2

這可以通過以下方式實現:

var lst = List.of(smth1, smth2, smth3);
lst.stream()
   .collect(Collectors.groupingBy(Something::get1, Collectors.groupingBy(Something::get3)))

如果我有任何想要應用於分組的任意方法列表怎么辦?

我在想這樣的事情(ofc。這不起作用,但你會明白的):

假設List<Function<Something, String>> groupingFunctions是我們想要應用於分組的方法列表。

var collector = groupingFunctions.stream()
                                 .reduce((f1, f2) -> Collectors.groupingBy(f1, Collectors.groupingBy(f2)))

進而

List.of(smth1, smth2, smth3).stream().collect(collector)

但這種方法行不通。 如何達到我想的結果?

你可以這樣做:

public static Collector createCollector(Function<A, String>... groupKeys) {
    Collector collector = Collectors.toList();
    for (int i = groupKeys.length - 1; i >= 0; i--) {
        collector = Collectors.groupingBy(groupKeys[i], collector);
    }
    return collector;
}

這為您提供了一個原始收集器,因此分組后的流結果也是原始的。

Collector collector = createCollector(Something::get1, Something::get2);

你可以像這樣使用這個collector

Object result = somethingList.stream().collect(collector);

因為您知道傳遞給收集器的groupingBy ,所以您可以將其轉換為適當的Map結果。 在這種情況下,應用了兩個groupingBy

Map<String, Map<String, List<Something>>> mapResult = (Map<String, Map<String, List<Something>>>) result

由於您不知道列表中有多少函數,因此您無法聲明反映嵌套的編譯時類型。 但是,即使使用收集器類型產生一些未知的結果類型,也無法以您想要的干凈功能方式進行組合。 你能得到的最接近的是

var collector = groupingFunctions.stream()
    .<Collector<Something,?,?>>reduce(
        Collectors.toList(),
        (c,f) -> Collectors.groupingBy(f, c),
        (c1,c2) -> { throw new UnsupportedOperationException("can't handle that"); });

這有兩個基本問題。 沒有辦法為兩個Collector實例提供有效的合並功能,因此雖然這可能適用於順序操作,但它不是一個干凈的解決方案。 此外,結果映射的嵌套順序將相反; 列表的最后一個函數將提供最外層地圖的鍵。

可能有辦法解決這個問題,但所有這些都會使代碼變得更加復雜。 將此與直接循環進行比較:

Collector<Something,?,?> collector = Collectors.toList();
for(var i = groupingFunctions.listIterator(groupingFunctions.size()); i.hasPrevious(); )
    collector = Collectors.groupingBy(i.previous(), collector);

你可以像這樣使用收集器

Object o = lst.stream().collect(collector);

但是需要instanceof和類型轉換來處理Map ......

使用反映分組功能的List鍵創建單個非嵌套Map會更清晰:

Map<List<String>,List<Something>> map = lst.stream().collect(Collectors.groupingBy(
    o -> groupingFunctions.stream().map(f -> f.apply(o))
                          .collect(Collectors.toUnmodifiableList())));

它允許查詢像map.get(List.of(arguments, matching, grouping, functions))這樣的條目

暫無
暫無

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

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