将多个`Collectors::groupBy`函数与Java流组合

将多个`Collectors::groupBy`函数与Java流组合,java,java-stream,collectors,Java,Java Stream,Collectors,我在正确组合多个收集器::groupingBy函数,然后将它们同时应用于给定输入时遇到问题 假设我有一个类实现了以下接口: interface Something { String get1(); String get2(); String get3(); String get4(); } 现在我可以从这个界面得到一些方法组合的列表,也就是说,这些列表可以是: [Something::get1,Something::get3],[Something::get2,

我在正确组合多个
收集器::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 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;
}
您可以像这样使用此
收集器

Object result = somethingList.stream().collect(collector);
因为您知道传递给收集器的
groupingBy
数量,所以可以将其转换为相应的
Map
结果。在这种情况下,将应用两个
groupingBy

Map<String, Map<String, List<Something>>> mapResult = (Map<String, Map<String, List<Something>>>) result
Map mapResult=(映射)结果

由于不知道列表中有多少函数,因此不能声明反映嵌套的编译时类型。但是,即使使用一个收集器类型生成一些未知的结果类型,组合它也不能以您想要的干净函数方式解决。你能得到的最接近的是

var collector = groupingFunctions.stream()
    .<Collector<Something,?,?>>reduce(
        Collectors.toList(),
        (c,f) -> Collectors.groupingBy(f, c),
        (c1,c2) -> { throw new UnsupportedOperationException("can't handle that"); });
您可以像这样使用收集器

Object o = lst.stream().collect(collector);
但是需要
instanceof
并键入cast来处理
Map
s

使用反映分组功能的
列表
键创建一个非嵌套的
映射
会更简洁:

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

它将允许查询诸如
map.get(List.of(参数、匹配、分组、函数))

最终结果是什么样子的?最终结果是一个嵌套的映射。最终
map
键值类型是4嵌套的,可以原始类型吗?如何使用结果,当你不知道它的类型时?@Holger-如果某个东西有一个属性<代码>列表子对象和一个属性<代码>字符串名称,我会将映射重新映射到某个对象,其中键是名称,列表是子对象,这样就不会有问题了。我只是想有一个通用的解决方案来创建一个深度嵌套的树状结构。实际上,我不能强制转换它,因为我不知道将应用多少groupingBy。它可以是1、2、5或任何其他数量的分组。另外,我想坚持函数方法,这就是我提到reduce的原因。我知道我可以循环查看函数列表,但我不想这样做。您可以使用
functional
方法重构
createCollector
。但是没有什么能解决这个问题。最后,您仍然需要知道铸造的
Map
类型
Object o = lst.stream().collect(collector);
Map<List<String>,List<Something>> map = lst.stream().collect(Collectors.groupingBy(
    o -> groupingFunctions.stream().map(f -> f.apply(o))
                          .collect(Collectors.toUnmodifiableList())));