[英]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.