Java 手动链接分组收集器

Java 手动链接分组收集器,java,java-8,grouping,java-stream,collectors,Java,Java 8,Grouping,Java Stream,Collectors,我想把一个人的名单分组。一个人有一些属性,如姓名、国家、城镇、zipcode等。我编写了静态代码,效果非常好: Object groupedData = data.stream().collect(groupingBy(Person::getName, Collectors.groupingBy(Person::getCountry, Collectors.groupingBy(Person::getTown)))); 但问题是,这不是动态的。有时我只想按名字和城镇分组,有时按属性分组。我该怎

我想把一个人的名单分组。一个人有一些属性,如姓名、国家、城镇、zipcode等。我编写了静态代码,效果非常好:

Object groupedData = data.stream().collect(groupingBy(Person::getName, Collectors.groupingBy(Person::getCountry, Collectors.groupingBy(Person::getTown))));

但问题是,这不是动态的。有时我只想按名字和城镇分组,有时按属性分组。我该怎么做?非Java 8解决方案也受欢迎。

您可以创建一个函数,使用任意数量的属性进行分组,并在循环中构造
groupingBy
-
收集器
,每次都将其以前的版本作为
下游
收集器传递

public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) {
    Iterator<Function<T, ?>> iter = Arrays.asList(groupers).iterator();
    Collector collector = Collectors.groupingBy(iter.next());
    while (iter.hasNext()) {
        collector = Collectors.groupingBy(iter.next(), collector);
    }
    return (Map) data.stream().collect(collector);
}
或者像这样,使用一个老式的
for
循环来反转函数中的数组,也就是说,您不必按相反的顺序传递Grouper(但这很难理解):

公共静态映射集合(列表数据、函数…分组){
收集器收集器=收集器.groupingBy(groupers[groupers.length-1]);
对于(int i=groupers.length-2;i>=0;i--){
收集器=收集器.groupingBy(groupers[i],收集器);
}
返回(映射)data.stream().collect(收集器);
}

这两种方法都将返回一个
映射,T>
,正如所建议的那样。

您可以通过一个列表进行分组,该列表由要分组的属性组成

想象一下,你想按姓名和国家分组。然后你可以使用:

Map<List<Object>, List<Person>> groupedData = 
    data.stream().collect(groupingBy(p -> Arrays.asList(p.getName(), p.getCountry())));
映射组数据=
data.stream().collect(groupingBy(p->Arrays.asList(p.getName(),p.getCountry()));

这是因为当两个列表以相同的顺序包含相同的元素时,它们被认为是相等的。因此,在生成的地图中,您将为每个不同的姓名/国家/地区对设置一个密钥,并作为具有这些特定姓名和国家/地区的人员列表的值。换句话说,它实际上不是说“按姓名分组,然后按国家分组”,而是说“按姓名和国家分组”。这样做的好处是你不会得到一张地图的地图,等等。

“有时我只想按名字和城镇分组,有时按属性分组。”-取决于用户输入或什么?另请参见“好处是你不会得到一张地图的地图”假设OP实际上不希望得到这样的结果……那么这个要求并不是问题的一部分@tobias_k。一个只有键、值的映射比一个内部映射级别未知的映射更容易使用。这是问题的一部分,因为OP的代码目前所做的是什么,“它工作得很好”,OP说。无论如何,我认为遵循哪种方法很大程度上取决于你想如何使用数据,当然,在某些情况下,你的方法更容易处理。拯救了我的一天!工作很顺利。谢谢为什么要使用所有迭代器样板文件?你可以简单地为每一个在array@Clashsoft问题是第一个条目必须以不同的方式处理。我的第一个版本使用的是
foreach
,但它需要一个三元组来处理第一个条目。
public static <T> Map collectMany(List<T> data, Function<T, ?>... groupers) {
    Collector collector = Collectors.groupingBy(groupers[groupers.length-1]);
    for (int i = groupers.length - 2; i >= 0; i--) {
        collector = Collectors.groupingBy(groupers[i], collector);
    }
    return (Map) data.stream().collect(collector);
}
Map<List<Object>, List<Person>> groupedData = 
    data.stream().collect(groupingBy(p -> Arrays.asList(p.getName(), p.getCountry())));