我在正确组合多个Collectors::groupingBy
函数然后将它们全部应用到给定输入时遇到问题。
假设我有一些类实现以下接口:
interface Something {
String get1();
String get2();
String get3();
String get4();
}
现在我可以从这个界面获得一些方法组合列表,即这些列表可以是:
[Something::get1, Something::get3]
,[Something::get2, Something::get1, Something::get3]
。
现在,有了这样的方法列表并列出了一些东西,我想把这些事情分组给吸气鬼。
我的意思是,例如,对于列表[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)
但这种方法不起作用。如何实现我想到的结果?
答案 0 :(得分:0)
你可以这样做:
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
答案 1 :(得分:0)
由于您不知道列表中有多少函数,因此无法声明反映嵌套的编译时类型。但即使使用产生某种未知结果类型的收集器类型,组合它也无法以您想要的干净功能方式解决。你能得到的最接近的是
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
s ...
使用反映分组功能的Map
密钥创建单个非嵌套List
会更清晰:
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))